mirror of
https://github.com/ENSL/NS.git
synced 2024-11-26 06:20:57 +00:00
3914 lines
96 KiB
C++
3914 lines
96 KiB
C++
//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
|
|
//
|
|
// The copyright to the contents herein is the property of Charles G. Cleveland.
|
|
// The contents may be used and/or copied only with the written permission of
|
|
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
|
|
// the agreement/contract under which the contents have been supplied.
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $Workfile: AvHSharedUtil.cpp $
|
|
// $Date: 2002/11/12 22:39:25 $
|
|
//
|
|
//-------------------------------------------------------------------------------
|
|
// $Log: AvHSharedUtil.cpp,v $
|
|
// Revision 1.33 2002/11/12 22:39:25 Flayra
|
|
// - Logging changes for Psychostats compatibility
|
|
//
|
|
// Revision 1.32 2002/10/24 21:42:32 Flayra
|
|
// - Don't give waypoint to selected troops when scanning
|
|
//
|
|
// Revision 1.31 2002/10/19 21:19:49 Flayra
|
|
// - Debugging info for linux
|
|
//
|
|
// Revision 1.30 2002/10/16 01:07:26 Flayra
|
|
// - Added official sizes that HL supports (ugh)
|
|
// - Added utility function for drawing range of ghost building, but it's unused
|
|
//
|
|
// Revision 1.29 2002/10/03 19:07:40 Flayra
|
|
// - Hives can never be blocked by func_resources
|
|
//
|
|
// Revision 1.28 2002/09/25 20:50:58 Flayra
|
|
// - Allow small items to be built on entities (health can be dropped right on players)
|
|
//
|
|
// Revision 1.27 2002/09/23 22:31:40 Flayra
|
|
// - Updated team 0 colors so dictation can be read in readyroom
|
|
// - Draw range for prototype lab, observatory and sensory chamber
|
|
// - Alien building rings
|
|
// - Don't allow non-resource buildings built too close to func_resources
|
|
// - Added heavy armor and jetpacks
|
|
//
|
|
// Revision 1.26 2002/09/09 20:06:43 Flayra
|
|
// - New observatory artwork
|
|
//
|
|
// Revision 1.25 2002/08/31 18:01:03 Flayra
|
|
// - Work at VALVe
|
|
//
|
|
// Revision 1.24 2002/08/16 02:47:06 Flayra
|
|
// - Fixed bug where not all the entities were iterated through
|
|
// - Support for ring-drawing (but not for all entities)
|
|
//
|
|
// Revision 1.23 2002/08/09 00:51:11 Flayra
|
|
// - Cleaned up useless special casing of tracetangible, fixed some problems where buildings could be built on players
|
|
//
|
|
// Revision 1.22 2002/08/02 21:50:05 Flayra
|
|
// - Made more general, by detecting all entities, not just players
|
|
//
|
|
// Revision 1.21 2002/07/24 18:45:43 Flayra
|
|
// - Linux and scripting changes
|
|
//
|
|
// Revision 1.20 2002/07/23 17:26:49 Flayra
|
|
// - Siege don't require a nearby turret factory, only add built buildings for range detection, build problems on client (point contents failing on rough surfaces)
|
|
//
|
|
// Revision 1.19 2002/07/08 17:17:44 Flayra
|
|
// - Reworked colors, moved functions into server util
|
|
//
|
|
// Revision 1.18 2002/07/01 21:46:39 Flayra
|
|
// - Added support for generic building ranges, fixed morphing problems
|
|
//
|
|
// Revision 1.17 2002/06/25 18:18:00 Flayra
|
|
// - Renamed buildings, better is-area-free detection
|
|
//
|
|
// Revision 1.16 2002/06/03 16:58:13 Flayra
|
|
// - Renamed weapons factory and armory, hive is now a buildable
|
|
//
|
|
// Revision 1.15 2002/05/28 18:13:43 Flayra
|
|
// - Changed "entity blocked by whatever" message to be logged for PR/screenshot purposes
|
|
//
|
|
// Revision 1.14 2002/05/23 02:33:20 Flayra
|
|
// - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development.
|
|
//
|
|
//===============================================================================
|
|
|
|
#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 "AvHSharedUtil.h"
|
|
#include "AvHSelectionHelper.h"
|
|
#include "AvHConstants.h"
|
|
|
|
#ifdef AVH_SERVER
|
|
#include "AvHPlayer.h"
|
|
#include "AvHServerUtil.h"
|
|
#include "AvHEntities.h"
|
|
#include "AvHWeldable.h"
|
|
#include "AvHGamerules.h"
|
|
#include "../dlls/cfuncwall.h"
|
|
//#include "../common/com_model.h"
|
|
//int NS_PointContents(const hull_t *hull, int num, float p[3]);
|
|
#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;
|
|
//extern DebugPointListType gSquareDebugLocations;
|
|
#endif
|
|
|
|
#include "../util/MathUtil.h"
|
|
#include "../util/STLUtil.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 "../common/com_model.h"
|
|
|
|
#include "AvHSpecials.h"
|
|
#include "AvHMarineEquipmentConstants.h"
|
|
#include "../dlls/turretconst.h"
|
|
#include "AvHMarineWeaponConstants.h"
|
|
#include "AvHHulls.h"
|
|
#include "AvHAlienEquipmentConstants.h"
|
|
|
|
#include "CollisionUtil.h"
|
|
/*
|
|
bool NS_BoxesOverlap(float origin1[3], float size1[3], float origin2[3], float size2[3]);
|
|
int NS_PointContents(const hull_t *hull, int num, float p[3]);
|
|
int NS_BoxContents(const hull_t *hull, int num, float mins[3], float maxs[3]);
|
|
int NS_GetValveHull(int inHull);
|
|
void NS_TraceLine(const hull_t* hull, float srcPoint[3], float dstPoint[3], trace_t* trace);
|
|
*/
|
|
#include <time.h>
|
|
extern playermove_t* pmove;
|
|
vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox);
|
|
|
|
|
|
vec3_t gPMDebugPoint;
|
|
|
|
int kTeamColors[iNumberOfTeamColors][3] =
|
|
{
|
|
{ 255, 255, 255 }, // White (default)
|
|
|
|
// { 125, 165, 210 }, // Blue (marine 1)
|
|
{ 0, 153, 255 }, // Blue (marine 1)
|
|
|
|
// { 255, 170, 0 }, // HL orange (alien 1)
|
|
{ 255, 160, 0 }, // HL orange (alien 1)
|
|
|
|
{ 145, 215, 140 }, // Green (marine 2)
|
|
{ 200, 90, 70 }, // Red (alien 2)
|
|
{ 255, 255, 255 } // White (spectator)
|
|
};
|
|
|
|
float kFTeamColors[iNumberOfTeamColors][3] =
|
|
{
|
|
{ kTeamColors[0][0] / 255.0f, kTeamColors[0][1] / 255.0f, kTeamColors[0][2] / 255.0f },
|
|
{ kTeamColors[1][0] / 255.0f, kTeamColors[1][1] / 255.0f, kTeamColors[1][2] / 255.0f },
|
|
{ kTeamColors[2][0] / 255.0f, kTeamColors[2][1] / 255.0f, kTeamColors[2][2] / 255.0f },
|
|
{ kTeamColors[3][0] / 255.0f, kTeamColors[3][1] / 255.0f, kTeamColors[3][2] / 255.0f },
|
|
{ kTeamColors[4][0] / 255.0f, kTeamColors[4][1] / 255.0f, kTeamColors[4][2] / 255.0f },
|
|
{ kTeamColors[5][0] / 255.0f, kTeamColors[5][1] / 255.0f, kTeamColors[5][2] / 255.0f }
|
|
};
|
|
|
|
|
|
// Official allowed sizes
|
|
// {0, 0, 0 } { 0, 0, 0 } 0x0x0
|
|
// { -16, -16, -18 } { 16, 16, 18 } 32x32x36
|
|
// { -16, -16, -36 } { 16, 16, 36 } 32x32x72
|
|
// { -32, -32, -32 } { 32, 32, 32 } 64x64x64
|
|
|
|
//#define kBuilding1MinSize Vector(-14.33, -14.84, 0.02)
|
|
////#define kBuilding1MaxSize Vector(21.61, 14.86, 66.9686)
|
|
//#define kBuilding1MaxSize Vector(14.33, 14.86, 66.9686)
|
|
//
|
|
//#define kBuilding2MinSize Vector(-25.0, -25.0, 0.02)
|
|
//#define kBuilding2MaxSize Vector(25.0, 25.0, 66.9686)
|
|
|
|
#define kResourceMinSize Vector(-16.0, -16.0, 0.0)
|
|
#define kResourceMaxSize Vector(16.0, 16.0, 66.9486)
|
|
|
|
// Tried 100, 110, still jitters. Shrink tower down a bit?
|
|
#define kAlienResourceMinSize Vector(-16.0, -16.0, 0.0)
|
|
#define kAlienResourceMaxSize Vector(16.0, 16.0, 100.0) // was Vector(16.0, 16.0, 80.7443)
|
|
|
|
//physent_t* AvHSUGetEntity(int inPhysIndex)
|
|
//{
|
|
// physent_t* theTarget = NULL;
|
|
// int i = 0;
|
|
//
|
|
// if(inPhysIndex > 0)
|
|
// {
|
|
// #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);
|
|
//
|
|
// for (i = 0; i < MAX_PHYSENTS; i++)
|
|
// //for (i = 0; i < pmove->numphysent; i++)
|
|
// {
|
|
// physent_t* thePotentialCandidate = gEngfuncs.pEventAPI->EV_GetPhysent(i);
|
|
// if ( thePotentialCandidate && (thePotentialCandidate->info == inPhysIndex))
|
|
// {
|
|
// theTarget = &pmove->physents[i];
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// //gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
// #endif
|
|
//
|
|
// // Check phys entities on server
|
|
// #ifdef AVH_SERVER
|
|
// for (i = 0; i < MAX_PHYSENTS; i++)
|
|
// //for (i = 0; i < pmove->numphysent; i++)
|
|
// {
|
|
// if ( pmove->physents[i].info == inPhysIndex )
|
|
// {
|
|
// theTarget = &pmove->physents[i];
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// //CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit);
|
|
// #endif
|
|
// }
|
|
//
|
|
// return theTarget;
|
|
//}
|
|
|
|
const char* AvHSHUGetClassNameFromUser3(AvHUser3 inUser3)
|
|
{
|
|
const char* theClassName = "";
|
|
|
|
switch(inUser3)
|
|
{
|
|
case AVH_USER3_MARINE_PLAYER:
|
|
theClassName = "soldier";
|
|
break;
|
|
|
|
case AVH_USER3_COMMANDER_PLAYER:
|
|
theClassName = "commander";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER1:
|
|
theClassName = "skulk";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER2:
|
|
theClassName = "gorge";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER3:
|
|
theClassName = "lerk";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER4:
|
|
theClassName = "fade";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER5:
|
|
theClassName = "onos";
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_EMBRYO:
|
|
theClassName = "gestate";
|
|
break;
|
|
}
|
|
|
|
return theClassName;
|
|
}
|
|
|
|
|
|
// Pass in -1 to get all entities with a non-zero iuser3
|
|
void AvHSHUGetEntities(int inUser3, EntityListType& outEntities)
|
|
{
|
|
#ifdef AVH_SERVER
|
|
FOR_ALL_BASEENTITIES();
|
|
if((theBaseEntity->pev->iuser3 == inUser3) || ((inUser3 == -1) && (theBaseEntity->pev->iuser3 > 0)))
|
|
{
|
|
outEntities.push_back(theBaseEntity->entindex());
|
|
}
|
|
END_FOR_ALL_BASEENTITIES();
|
|
|
|
#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->iuser3 == inUser3) || ((inUser3 == -1) && (theEntity->iuser3 > 0)))
|
|
{
|
|
outEntities.push_back(i);
|
|
}
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
#endif
|
|
}
|
|
|
|
bool AvHSHUGetEntityLocation(int inEntity, vec3_t& outLocation)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
#ifdef AVH_SERVER
|
|
edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
|
|
if(pEdict && !pEdict->free)
|
|
{
|
|
CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
|
|
if(theEntity)
|
|
{
|
|
outLocation = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs);
|
|
theSuccess = true;
|
|
}
|
|
}
|
|
#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 = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
if(theEntity)
|
|
{
|
|
outLocation = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs);
|
|
theSuccess = true;
|
|
}
|
|
#endif
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
bool AvHSHUGetEntityIUser3(int inEntity, AvHUser3& outIUser3)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
#ifdef AVH_SERVER
|
|
edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
|
|
if(pEdict && !pEdict->free)
|
|
{
|
|
CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
|
|
if(theEntity)
|
|
{
|
|
outIUser3 = (AvHUser3)theEntity->pev->iuser3;
|
|
theSuccess = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);
|
|
|
|
physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
|
|
if(theEntity)
|
|
{
|
|
outIUser3 = (AvHUser3)theEntity->iuser3;
|
|
theSuccess = true;
|
|
}
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
#endif
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
bool AvHSHUGetEntityIUser4(int inEntity, int& outIUser4)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
#ifdef AVH_SERVER
|
|
edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
|
|
if(pEdict && !pEdict->free)
|
|
{
|
|
CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
|
|
if(theEntity)
|
|
{
|
|
outIUser4 = theEntity->pev->iuser4;
|
|
theSuccess = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
|
|
if(theEntity)
|
|
{
|
|
outIUser4 = theEntity->iuser4;
|
|
theSuccess = true;
|
|
}
|
|
#endif
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox)
|
|
{
|
|
vec3_t outLocation;
|
|
VectorCopy(inLocation, outLocation);
|
|
|
|
if((outLocation.x == outLocation.y) && (outLocation.y == outLocation.z) && (outLocation.z == 0.0f))
|
|
{
|
|
outLocation.x = (inMinBox.x + inMaxBox.x) * 0.5f;
|
|
outLocation.y = (inMinBox.y + inMaxBox.y) * 0.5f;
|
|
outLocation.z = (inMinBox.z + inMaxBox.z) * 0.5f;
|
|
}
|
|
return outLocation;
|
|
}
|
|
|
|
// Returns range of user3, whatever that means. Returns -1 for no meaningful range.
|
|
int AvHSHUGetDrawRangeForUser3(AvHUser3 inUser3)
|
|
{
|
|
int theRange = -1;
|
|
|
|
switch(inUser3)
|
|
{
|
|
case AVH_USER3_COMMANDER_STATION:
|
|
theRange = BALANCE_VAR(kCommandStationBuildDistance);
|
|
break;
|
|
|
|
case AVH_USER3_FUNC_RESOURCE:
|
|
theRange = BALANCE_VAR(kResourceTowerBuildDistanceTolerance);
|
|
break;
|
|
|
|
case AVH_USER3_TURRET_FACTORY:
|
|
case AVH_USER3_ADVANCED_TURRET_FACTORY:
|
|
theRange = BALANCE_VAR(kTurretFactoryBuildDistance);
|
|
break;
|
|
|
|
case AVH_USER3_ARMORY:
|
|
case AVH_USER3_ADVANCED_ARMORY:
|
|
theRange = BALANCE_VAR(kArmoryBuildDistance);
|
|
break;
|
|
|
|
case AVH_USER3_TURRET:
|
|
theRange = BALANCE_VAR(kTurretRange);
|
|
break;
|
|
|
|
case AVH_USER3_SIEGETURRET:
|
|
// TODO: Figure out a way to return minimum range also?
|
|
theRange = BALANCE_VAR(kSiegeTurretRange);
|
|
break;
|
|
|
|
case AVH_USER3_PROTOTYPE_LAB:
|
|
theRange = BALANCE_VAR(kArmorDropRange);
|
|
break;
|
|
|
|
case AVH_USER3_OBSERVATORY:
|
|
theRange = BALANCE_VAR(kObservatoryXYDetectionRadius);
|
|
break;
|
|
|
|
case AVH_USER3_SENSORY_CHAMBER:
|
|
theRange = BALANCE_VAR(kSensoryChamberRange);
|
|
break;
|
|
}
|
|
|
|
return theRange;
|
|
}
|
|
|
|
int AvHSHUGetDrawRangeForMessageID(AvHMessageID inMessageID)
|
|
{
|
|
AvHUser3 theUser3 = AVH_USER3_NONE;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_COMMANDSTATION:
|
|
theUser3 = AVH_USER3_COMMANDER_STATION;
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
theUser3 = AVH_USER3_TURRET;
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
theUser3 = AVH_USER3_SIEGETURRET;
|
|
break;
|
|
|
|
case BUILD_OBSERVATORY:
|
|
theUser3 = AVH_USER3_OBSERVATORY;
|
|
break;
|
|
|
|
case BUILD_TURRET_FACTORY:
|
|
theUser3 = AVH_USER3_TURRET_FACTORY;
|
|
break;
|
|
|
|
case BUILD_PROTOTYPE_LAB:
|
|
theUser3 = AVH_USER3_PROTOTYPE_LAB;
|
|
break;
|
|
|
|
case BUILD_ARMORY:
|
|
theUser3 = AVH_USER3_ARMORY;
|
|
break;
|
|
}
|
|
|
|
return AvHSHUGetDrawRangeForUser3(theUser3);
|
|
}
|
|
|
|
|
|
// Specify whether we draw rings for this entity or not, and draw them at different sizes for aesthetics. The scalar might go away when the
|
|
// artwork all has the correct bounding boxes.
|
|
bool AvHSHUGetDrawRingsForUser3(AvHUser3 inUser3, float& outScalar)
|
|
{
|
|
bool theDrawRings = false;
|
|
float theScalar = 1.0f;
|
|
|
|
switch(inUser3)
|
|
{
|
|
case AVH_USER3_MARINE_PLAYER:
|
|
theScalar = .9f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_RESTOWER:
|
|
theScalar = 1.6f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_INFANTRYPORTAL:
|
|
theScalar = 1.35f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_ARMORY:
|
|
case AVH_USER3_ADVANCED_ARMORY:
|
|
theScalar = 1.35f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_COMMANDER_STATION:
|
|
theScalar = 1.4f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_PHASEGATE:
|
|
theScalar = 2.0f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_OBSERVATORY:
|
|
theScalar = .9f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_TURRET_FACTORY:
|
|
case AVH_USER3_ADVANCED_TURRET_FACTORY:
|
|
theScalar = 1.6f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_TURRET:
|
|
theScalar = .7f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_NUKEPLANT:
|
|
theScalar = 1.5f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_ARMSLAB:
|
|
case AVH_USER3_PROTOTYPE_LAB:
|
|
case AVH_USER3_CHEMLAB:
|
|
case AVH_USER3_MEDLAB:
|
|
case AVH_USER3_SIEGETURRET:
|
|
theScalar = 1.3f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
// Alien buildings
|
|
case AVH_USER3_DEFENSE_CHAMBER:
|
|
case AVH_USER3_OFFENSE_CHAMBER:
|
|
case AVH_USER3_MOVEMENT_CHAMBER:
|
|
case AVH_USER3_SENSORY_CHAMBER:
|
|
case AVH_USER3_ALIENRESTOWER:
|
|
theScalar = 1.3f;
|
|
theDrawRings = true;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER1:
|
|
theScalar = .6f;
|
|
theDrawRings = true;
|
|
break;
|
|
case AVH_USER3_ALIEN_PLAYER2:
|
|
theScalar = 1.0f;
|
|
theDrawRings = true;
|
|
break;
|
|
case AVH_USER3_ALIEN_PLAYER3:
|
|
theScalar = 1.0;
|
|
theDrawRings = true;
|
|
break;
|
|
case AVH_USER3_ALIEN_PLAYER4:
|
|
theScalar = 1.3f;
|
|
theDrawRings = true;
|
|
break;
|
|
case AVH_USER3_ALIEN_PLAYER5:
|
|
theScalar = 1.2f;
|
|
theDrawRings = true;
|
|
break;
|
|
}
|
|
|
|
if(theDrawRings)
|
|
{
|
|
outScalar = theScalar;
|
|
}
|
|
|
|
return theDrawRings;
|
|
}
|
|
|
|
bool AvHSHUGetBuildRegions(AvHMessageID inMessageID, EntityListType& outEntities, IntList& outRanges, float& outZAdjustment, bool& outSnapToLocation)
|
|
{
|
|
bool theFoundTech = false;
|
|
|
|
typedef vector<AvHUser3> User3ListType;
|
|
User3ListType theUser3s;
|
|
|
|
outZAdjustment = 0.0f;
|
|
outSnapToLocation = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_RESOURCES:
|
|
case ALIEN_BUILD_RESOURCES:
|
|
theUser3s.push_back(AVH_USER3_FUNC_RESOURCE);
|
|
outZAdjustment = (kFuncResourceMaxSize.z - kFuncResourceMinSize.z);
|
|
outSnapToLocation = true;
|
|
break;
|
|
|
|
case BUILD_INFANTRYPORTAL:
|
|
theUser3s.push_back(AVH_USER3_COMMANDER_STATION);
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
theUser3s.push_back(AVH_USER3_TURRET_FACTORY);
|
|
theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY);
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY);
|
|
break;
|
|
|
|
case BUILD_MINES:
|
|
case BUILD_WELDER:
|
|
case BUILD_SHOTGUN:
|
|
theUser3s.push_back(AVH_USER3_ARMORY);
|
|
theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY);
|
|
break;
|
|
|
|
case BUILD_HMG:
|
|
case BUILD_GRENADE_GUN:
|
|
theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY);
|
|
break;
|
|
|
|
case BUILD_HEAVY:
|
|
case BUILD_JETPACK:
|
|
theUser3s.push_back(AVH_USER3_PROTOTYPE_LAB);
|
|
break;
|
|
}
|
|
|
|
if(theUser3s.size() > 0)
|
|
{
|
|
for(User3ListType::iterator theUser3Iter = theUser3s.begin(); theUser3Iter != theUser3s.end(); theUser3Iter++)
|
|
{
|
|
EntityListType theEntities;
|
|
AvHSHUGetEntities(*theUser3Iter, theEntities);
|
|
|
|
for(EntityListType::iterator theEntityIter = theEntities.begin(); theEntityIter != theEntities.end(); theEntityIter++)
|
|
{
|
|
// Only add buildings that are fully built
|
|
int theIuser4 = 0;
|
|
if(AvHSHUGetEntityIUser4(*theEntityIter, theIuser4))
|
|
{
|
|
if(!GetHasUpgrade(theIuser4, MASK_BUILDABLE))
|
|
{
|
|
outEntities.push_back(*theEntityIter);
|
|
outRanges.push_back(AvHSHUGetDrawRangeForUser3(*theUser3Iter));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
theFoundTech = true;
|
|
}
|
|
|
|
return theFoundTech;
|
|
}
|
|
|
|
|
|
// : 0000291 -- allows listed structures to be dropped on resource towers
|
|
bool AvHSHUGetIsDroppableOnRTs(AvHMessageID inMessageID)
|
|
{
|
|
switch (inMessageID)
|
|
{
|
|
case BUILD_HEALTH:
|
|
case BUILD_AMMO:
|
|
case BUILD_MINES:
|
|
case BUILD_WELDER:
|
|
case BUILD_SHOTGUN:
|
|
case BUILD_HMG:
|
|
case BUILD_GRENADE_GUN:
|
|
case BUILD_CAT:
|
|
case BUILD_HEAVY:
|
|
case BUILD_JETPACK:
|
|
case BUILD_RESOURCES:
|
|
case ALIEN_BUILD_RESOURCES:
|
|
case ALIEN_BUILD_HIVE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
// :
|
|
|
|
bool AvHSHUGetIsMarineStructure(AvHMessageID inMessageID)
|
|
{
|
|
|
|
switch (inMessageID)
|
|
{
|
|
|
|
case BUILD_INFANTRYPORTAL:
|
|
case BUILD_RESOURCES:
|
|
case BUILD_TURRET_FACTORY:
|
|
case BUILD_ARMSLAB:
|
|
case BUILD_PROTOTYPE_LAB:
|
|
case BUILD_ARMORY:
|
|
case BUILD_NUKE_PLANT:
|
|
case BUILD_OBSERVATORY:
|
|
case BUILD_PHASEGATE:
|
|
case BUILD_TURRET:
|
|
case BUILD_SIEGE:
|
|
case BUILD_COMMANDSTATION:
|
|
return true;
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool AvHSHUGetIsMarineStructure(AvHUser3 inUser3)
|
|
{
|
|
|
|
switch (inUser3)
|
|
{
|
|
case AVH_USER3_COMMANDER_STATION:
|
|
case AVH_USER3_TURRET_FACTORY:
|
|
case AVH_USER3_ARMORY:
|
|
case AVH_USER3_ADVANCED_ARMORY:
|
|
case AVH_USER3_ARMSLAB:
|
|
case AVH_USER3_PROTOTYPE_LAB:
|
|
case AVH_USER3_OBSERVATORY:
|
|
case AVH_USER3_CHEMLAB:
|
|
case AVH_USER3_MEDLAB:
|
|
case AVH_USER3_NUKEPLANT:
|
|
case AVH_USER3_TURRET:
|
|
case AVH_USER3_SIEGETURRET:
|
|
case AVH_USER3_RESTOWER:
|
|
case AVH_USER3_INFANTRYPORTAL:
|
|
case AVH_USER3_PHASEGATE:
|
|
case AVH_USER3_ADVANCED_TURRET_FACTORY:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
void AvHSHUGetMinBuildRadiusViolations(AvHMessageID inMessageID, vec3_t& inLocation, EntityListType& outViolations)
|
|
{
|
|
|
|
// Enforce a minimum build radius for marine structures.
|
|
|
|
if (AvHSHUGetIsMarineStructure(inMessageID))
|
|
{
|
|
EntityListType theEntities;
|
|
AvHSHUGetEntities(-1, theEntities);
|
|
|
|
vec3_t theMinSize, theMaxSize;
|
|
|
|
AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize);
|
|
float theMaxRadius1 = max(-min(theMinSize.x, theMinSize.y), max(theMaxSize.x, theMaxSize.y));
|
|
|
|
for (EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++)
|
|
{
|
|
|
|
AvHUser3 theUser3;
|
|
AvHSHUGetEntityIUser3(*theIter, theUser3);
|
|
|
|
bool theEntityCouldBlock = AvHSHUGetIsMarineStructure(theUser3);
|
|
if(inMessageID != BUILD_RESOURCES)
|
|
{
|
|
theEntityCouldBlock |= (theUser3 == AVH_USER3_FUNC_RESOURCE);
|
|
}
|
|
|
|
bool theEntityIsSolid = false;
|
|
|
|
#ifdef AVH_SERVER
|
|
edict_t* theEntity = INDEXENT(*theIter);
|
|
|
|
if(theEntity)
|
|
theEntityIsSolid = (theEntity->v.solid == SOLID_BBOX);
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter);
|
|
|
|
if(theEntity)
|
|
theEntityIsSolid = (theEntity->solid == SOLID_BBOX);
|
|
|
|
#endif
|
|
// : 0000291
|
|
// It's possible to place "on" marines if you're offset a little from center. This code and
|
|
// associated changes below and in AvHHudRender.cpp is to enforce a build distance around players
|
|
// in the same way as buildings to prevent this exploit.
|
|
if (theUser3 == AVH_USER3_MARINE_PLAYER)
|
|
{
|
|
theEntityIsSolid = true;
|
|
theEntityCouldBlock = true;
|
|
}
|
|
|
|
if (theEntityCouldBlock && theEntityIsSolid)
|
|
{
|
|
AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize);
|
|
float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y));
|
|
|
|
vec3_t theLocation;
|
|
if(AvHSHUGetEntityLocation(*theIter, theLocation))
|
|
{
|
|
vec3_t theXYInLocation = inLocation;
|
|
vec3_t theXYTheLocation = theLocation;
|
|
|
|
//theXYInLocation.z = 0;
|
|
//theXYTheLocation.z = 0;
|
|
|
|
float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation);
|
|
// : 0000291
|
|
// It's possible to place "on" marines if you're offset a little from center. This code and
|
|
// associated changes above and in AvHHudRender.cpp is to enforce a build distance around players
|
|
// in the same way as buildings to prevent this exploit.
|
|
float theMinMarineBuildDistance;
|
|
if (theUser3 == AVH_USER3_MARINE_PLAYER) {
|
|
theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance);
|
|
}
|
|
else
|
|
{
|
|
theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance);
|
|
}
|
|
// :
|
|
if (theDistance < theMinMarineBuildDistance + theMaxRadius1 + theMaxRadius2)
|
|
{
|
|
outViolations.push_back(*theIter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AvHSHUGetAreSpecialBuildingRequirementsMet(AvHMessageID inMessageID, vec3_t& inLocation)
|
|
{
|
|
bool theRequirementsMet = false;
|
|
EntityListType theEntities;
|
|
IntList theDistanceRequirements;
|
|
float theZAdjustment = 0.0f;
|
|
bool theSnapToLocation = false;
|
|
|
|
if(inMessageID == ALIEN_BUILD_HIVE)
|
|
{
|
|
// Look for inactive hives within radius
|
|
EntityListType theEntities;
|
|
|
|
// Look for a unoccupied hive spot within range
|
|
AvHSHUGetEntities(AVH_USER3_HIVE, theEntities);
|
|
|
|
for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++)
|
|
{
|
|
vec3_t theLocation;
|
|
if(AvHSHUGetEntityLocation(*theIter, theLocation))
|
|
{
|
|
// Set z's equal to check 2D distance only
|
|
inLocation.z = theLocation.z;
|
|
float theDistance = VectorDistance((float*)&inLocation, (float*)&theLocation);
|
|
|
|
if(theDistance <= kHiveXYDistanceTolerance)
|
|
{
|
|
// Make sure this hive isn't already active
|
|
#ifdef AVH_SERVER
|
|
CBaseEntity* theEntity = AvHSUGetEntityFromIndex(*theIter);
|
|
ASSERT(theEntity);
|
|
if(theEntity->pev->team == 0)
|
|
{
|
|
#endif
|
|
|
|
theRequirementsMet = true;
|
|
inLocation = theLocation;
|
|
|
|
#ifdef AVH_SERVER
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(AvHSHUGetBuildRegions(inMessageID, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation))
|
|
{
|
|
ASSERT(theEntities.size() == theDistanceRequirements.size());
|
|
vec3_t theBestLocation;
|
|
|
|
int i = 0;
|
|
float theClosestDistance = kMaxMapDimension;
|
|
for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++, i++)
|
|
{
|
|
vec3_t theLocation;
|
|
if(AvHSHUGetEntityLocation(*theIter, theLocation))
|
|
{
|
|
// Only check xy distance
|
|
vec3_t theXYInLocation = inLocation;
|
|
vec3_t theXYTheLocation = theLocation;
|
|
theXYInLocation.z = 0;
|
|
theXYTheLocation.z = 0;
|
|
|
|
float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation);
|
|
|
|
int theDistanceRequirement = theDistanceRequirements[i];
|
|
if((theDistance <= theDistanceRequirement) || (theDistanceRequirement == -1))
|
|
{
|
|
// Pick the closest one, in case there are multiples in range
|
|
if(theDistance < theClosestDistance)
|
|
{
|
|
theClosestDistance = theDistance;
|
|
VectorCopy(theLocation,theBestLocation);
|
|
theRequirementsMet = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(theRequirementsMet && theSnapToLocation)
|
|
{
|
|
inLocation = theBestLocation;
|
|
inLocation.z += theZAdjustment;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theRequirementsMet = true;
|
|
}
|
|
|
|
EntityListType theBuildRadiusViolations;
|
|
AvHSHUGetMinBuildRadiusViolations(inMessageID, inLocation, theBuildRadiusViolations);
|
|
|
|
if (theBuildRadiusViolations.size() > 0)
|
|
{
|
|
theRequirementsMet = false;
|
|
}
|
|
|
|
// Anti-llama/newbie tactic: don't allow non-resource buildings to be placed such that they block access to nozzles
|
|
// Make sure generic building isn't being placed on top of resource nozzles
|
|
// : 0000291
|
|
// allow equipment, rts and hives to be dropped around nodes
|
|
if(AvHSHUGetIsDroppableOnRTs(inMessageID) == false)
|
|
{
|
|
// :
|
|
// If building is too close to an empty nozzle, don't allow it
|
|
float theResourceBuildingRadius, theTotalMinRadius;
|
|
vec3_t theMinSize, theMaxSize, theMinRadius, theFlattenedInLocation, theLocation;
|
|
|
|
theFlattenedInLocation[0] = inLocation[0];
|
|
theFlattenedInLocation[1] = inLocation[1];
|
|
theFlattenedInLocation[2] = 0;
|
|
|
|
theResourceBuildingRadius = 60;
|
|
|
|
EntityListType theEntities;
|
|
AvHSHUGetEntities(AVH_USER3_FUNC_RESOURCE,theEntities);
|
|
if(AvHSHUGetSizeForTech(inMessageID,theMinSize,theMaxSize))
|
|
{
|
|
EntityListType::iterator end = theEntities.end();
|
|
for(EntityListType::iterator current = theEntities.begin(); current < end; ++current)
|
|
{
|
|
if(AvHSHUGetEntityLocation(*current,theLocation))
|
|
{
|
|
//flatten to 2 dimensions
|
|
theLocation[2] = 0;
|
|
|
|
//space = radius of both buildings combined
|
|
theTotalMinRadius = theResourceBuildingRadius;
|
|
theTotalMinRadius += max(-min(theMinSize.x,theMinSize.y),max(theMaxSize.x,theMaxSize.y));
|
|
|
|
if(VectorDistance((float*)&theFlattenedInLocation,(float*)&theLocation) < theTotalMinRadius)
|
|
{
|
|
theRequirementsMet = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return theRequirementsMet;
|
|
}
|
|
|
|
bool AvHSHUGetBuildTechClassName(AvHMessageID inMessageID, char*& outClassName)
|
|
{
|
|
bool theSuccess = true;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
// Buildings
|
|
case BUILD_RESOURCES:
|
|
outClassName = kwsResourceTower;
|
|
break;
|
|
|
|
//case BUILD_REINFORCEMENTS:
|
|
// outClassName = kwsInfantryPortal;
|
|
// break;
|
|
|
|
case BUILD_INFANTRYPORTAL:
|
|
outClassName = kwsInfantryPortal;
|
|
break;
|
|
|
|
case BUILD_COMMANDSTATION:
|
|
outClassName = kwsTeamCommand;
|
|
break;
|
|
|
|
case BUILD_TURRET_FACTORY:
|
|
outClassName = kwsTurretFactory;
|
|
break;
|
|
|
|
case BUILD_ARMSLAB:
|
|
outClassName = kwsArmsLab;
|
|
break;
|
|
|
|
case BUILD_PROTOTYPE_LAB:
|
|
outClassName = kwsPrototypeLab;
|
|
break;
|
|
|
|
case BUILD_ARMORY:
|
|
outClassName = kwsArmory;
|
|
break;
|
|
|
|
case ARMORY_UPGRADE:
|
|
outClassName = kwsAdvancedArmory;
|
|
break;
|
|
|
|
case BUILD_NUKE_PLANT:
|
|
outClassName = kwsNukePlant;
|
|
break;
|
|
|
|
case BUILD_OBSERVATORY:
|
|
outClassName = kwsObservatory;
|
|
break;
|
|
|
|
case BUILD_SCAN:
|
|
outClassName = kwsScan;
|
|
break;
|
|
|
|
case BUILD_PHASEGATE:
|
|
outClassName = kwsPhaseGate;
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
outClassName = kwsDeployedTurret;
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
outClassName = kwsSiegeTurret;
|
|
break;
|
|
|
|
// Equipment
|
|
case BUILD_HEALTH:
|
|
outClassName = kwsHealth;
|
|
break;
|
|
|
|
case BUILD_CAT:
|
|
outClassName = kwsCatalyst;
|
|
break;
|
|
|
|
case BUILD_JETPACK:
|
|
outClassName = kwsJetpack;
|
|
break;
|
|
|
|
case BUILD_HEAVY:
|
|
outClassName = kwsHeavyArmor;
|
|
break;
|
|
|
|
case BUILD_AMMO:
|
|
outClassName = kwsGenericAmmo;
|
|
break;
|
|
|
|
case BUILD_WELDER:
|
|
outClassName = kwsWelder;
|
|
break;
|
|
|
|
case BUILD_MINES:
|
|
outClassName = kwsMine;
|
|
break;
|
|
|
|
case BUILD_SHOTGUN:
|
|
outClassName = kwsShotGun;
|
|
break;
|
|
|
|
case BUILD_HMG:
|
|
outClassName = kwsHeavyMachineGun;
|
|
break;
|
|
|
|
case BUILD_NUKE:
|
|
outClassName = kwsNuke;
|
|
break;
|
|
|
|
case BUILD_GRENADE_GUN:
|
|
outClassName = kwsGrenadeGun;
|
|
break;
|
|
|
|
//case BUILD_MEDKIT:
|
|
// break;
|
|
|
|
case ALIEN_BUILD_RESOURCES:
|
|
outClassName = kwsAlienResourceTower;
|
|
break;
|
|
|
|
case ALIEN_BUILD_OFFENSE_CHAMBER:
|
|
outClassName = kwsOffenseChamber;
|
|
break;
|
|
|
|
case ALIEN_BUILD_DEFENSE_CHAMBER:
|
|
outClassName = kwsDefenseChamber;
|
|
break;
|
|
|
|
case ALIEN_BUILD_SENSORY_CHAMBER:
|
|
outClassName = kwsSensoryChamber;
|
|
break;
|
|
|
|
case ALIEN_BUILD_MOVEMENT_CHAMBER:
|
|
outClassName = kwsMovementChamber;
|
|
break;
|
|
|
|
case ALIEN_BUILD_HIVE:
|
|
outClassName = kesTeamHive;
|
|
break;
|
|
|
|
default:
|
|
theSuccess = false;
|
|
break;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUGetResearchTechName(AvHMessageID inResearchID, char*& outResearchTechName)
|
|
{
|
|
bool theSuccess = true;
|
|
|
|
switch(inResearchID)
|
|
{
|
|
case RESEARCH_ELECTRICAL:
|
|
outResearchTechName = "research_electrical";
|
|
break;
|
|
|
|
case RESEARCH_ARMOR_ONE:
|
|
outResearchTechName = "research_armorl1";
|
|
break;
|
|
|
|
case RESEARCH_ARMOR_TWO:
|
|
outResearchTechName = "research_armorl2";
|
|
break;
|
|
|
|
case RESEARCH_ARMOR_THREE:
|
|
outResearchTechName = "research_armorl3";
|
|
break;
|
|
|
|
case RESEARCH_WEAPONS_ONE:
|
|
outResearchTechName = "research_weaponsl1";
|
|
break;
|
|
|
|
case RESEARCH_WEAPONS_TWO:
|
|
outResearchTechName = "research_weaponsl2";
|
|
break;
|
|
|
|
case RESEARCH_WEAPONS_THREE:
|
|
outResearchTechName = "research_weaponsl3";
|
|
break;
|
|
|
|
case ARMORY_UPGRADE:
|
|
outResearchTechName = "research_advarmory";
|
|
break;
|
|
|
|
case TURRET_FACTORY_UPGRADE:
|
|
outResearchTechName = "research_advturretfactory";
|
|
break;
|
|
|
|
case RESEARCH_JETPACKS:
|
|
outResearchTechName = "research_jetpacks";
|
|
break;
|
|
|
|
case RESEARCH_HEAVYARMOR:
|
|
outResearchTechName = "research_heavyarmor";
|
|
break;
|
|
|
|
case RESEARCH_DISTRESSBEACON:
|
|
outResearchTechName = "research_distressbeacon";
|
|
break;
|
|
|
|
case RESEARCH_HEALTH:
|
|
outResearchTechName = "research_health";
|
|
break;
|
|
|
|
case RESEARCH_CATALYSTS:
|
|
outResearchTechName = "research_catalysts";
|
|
break;
|
|
|
|
case MESSAGE_CANCEL:
|
|
outResearchTechName = "research_cancel";
|
|
break;
|
|
|
|
case RESEARCH_MOTIONTRACK:
|
|
outResearchTechName = "research_motiontracking";
|
|
break;
|
|
|
|
case RESEARCH_PHASETECH:
|
|
outResearchTechName = "research_phasetech";
|
|
break;
|
|
|
|
case RESEARCH_GRENADES:
|
|
outResearchTechName = "research_grenades";
|
|
break;
|
|
|
|
default:
|
|
theSuccess = false;
|
|
break;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition)
|
|
{
|
|
bool theSuccess = false;
|
|
vec3_t thePosition;
|
|
float theX, theY;
|
|
|
|
#ifdef AVH_CLIENT
|
|
theSuccess = gHUD.GetCenterPositionForGroup(inGroupNumber, thePosition);
|
|
theX = thePosition[0];
|
|
theY = thePosition[1];
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
|
|
// Loop through players, find the closest player to inPlayerOrigin, to see which player is being predicted. Is there a better way?
|
|
AvHPlayer* theClosestPlayer = NULL;
|
|
float theClosestDistance = 10000;
|
|
|
|
FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
|
|
float theDistance = VectorDistance(theEntity->pev->origin, inPlayerOrigin);
|
|
if(theDistance < theClosestDistance)
|
|
{
|
|
theClosestPlayer = theEntity;
|
|
theClosestDistance = theDistance;
|
|
}
|
|
END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
|
|
|
|
if(theClosestPlayer)
|
|
{
|
|
theSuccess = theClosestPlayer->GetCenterPositionForGroup(inGroupNumber, theX, theY);
|
|
}
|
|
#endif
|
|
|
|
if(theSuccess)
|
|
{
|
|
outCenterPosition[0] = theX;
|
|
outCenterPosition[1] = theY;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
bool AvHSHUGetIsBuilding(AvHMessageID inMessageID)
|
|
{
|
|
bool theIsBuilding = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
// Buildings
|
|
case BUILD_RESOURCES:
|
|
case BUILD_INFANTRYPORTAL:
|
|
case BUILD_COMMANDSTATION:
|
|
case BUILD_TURRET_FACTORY:
|
|
case BUILD_ARMSLAB:
|
|
case BUILD_PROTOTYPE_LAB:
|
|
case BUILD_ARMORY:
|
|
case BUILD_NUKE_PLANT:
|
|
case BUILD_OBSERVATORY:
|
|
case BUILD_PHASEGATE:
|
|
case BUILD_TURRET:
|
|
case BUILD_SIEGE:
|
|
|
|
// Alien buildings
|
|
case ALIEN_BUILD_OFFENSE_CHAMBER:
|
|
case ALIEN_BUILD_DEFENSE_CHAMBER:
|
|
case ALIEN_BUILD_SENSORY_CHAMBER:
|
|
case ALIEN_BUILD_MOVEMENT_CHAMBER:
|
|
case ALIEN_BUILD_HIVE:
|
|
theIsBuilding = true;
|
|
break;
|
|
}
|
|
|
|
return theIsBuilding;
|
|
}
|
|
|
|
bool AvHSHUGetIsBuildTech(AvHMessageID inMessageID)
|
|
{
|
|
bool theIsBuildTech = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
// Buildings
|
|
case BUILD_RESOURCES:
|
|
case BUILD_INFANTRYPORTAL:
|
|
case BUILD_COMMANDSTATION:
|
|
case BUILD_TURRET_FACTORY:
|
|
case BUILD_ARMSLAB:
|
|
case BUILD_PROTOTYPE_LAB:
|
|
case BUILD_ARMORY:
|
|
//case UPGRADE_ADVANCED_WEAPON_FACTORY:
|
|
case BUILD_NUKE_PLANT:
|
|
case BUILD_OBSERVATORY:
|
|
case BUILD_SCAN:
|
|
case BUILD_PHASEGATE:
|
|
case BUILD_TURRET:
|
|
case BUILD_SIEGE:
|
|
case BUILD_HEAVY:
|
|
case BUILD_JETPACK:
|
|
|
|
// Equipment
|
|
case BUILD_AMMO:
|
|
case BUILD_HEALTH:
|
|
case BUILD_CAT:
|
|
case BUILD_WELDER:
|
|
case BUILD_MINES:
|
|
case BUILD_SHOTGUN:
|
|
case BUILD_HMG:
|
|
case BUILD_NUKE:
|
|
case BUILD_GRENADE_GUN:
|
|
//case BUILD_MEDKIT:
|
|
//case BUILD_STIMPACK:
|
|
|
|
// Alien buildings
|
|
case ALIEN_BUILD_OFFENSE_CHAMBER:
|
|
case ALIEN_BUILD_DEFENSE_CHAMBER:
|
|
case ALIEN_BUILD_SENSORY_CHAMBER:
|
|
case ALIEN_BUILD_MOVEMENT_CHAMBER:
|
|
theIsBuildTech = true;
|
|
break;
|
|
}
|
|
|
|
return theIsBuildTech;
|
|
}
|
|
|
|
bool AvHSHUGetIsWeaponFocusable(AvHWeaponID inWeaponID)
|
|
{
|
|
bool theIsFocusable = false;
|
|
|
|
switch(inWeaponID)
|
|
{
|
|
case AVH_WEAPON_BITE:
|
|
case AVH_WEAPON_SPIT:
|
|
case AVH_WEAPON_BITE2:
|
|
case AVH_WEAPON_SWIPE:
|
|
case AVH_WEAPON_CLAWS:
|
|
theIsFocusable = true;
|
|
break;
|
|
}
|
|
|
|
return theIsFocusable;
|
|
}
|
|
|
|
bool AvHSHUGetDoesTechCostEnergy(AvHMessageID inMessageID)
|
|
{
|
|
bool theTechCostsEnergy = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_SCAN:
|
|
theTechCostsEnergy = true;
|
|
break;
|
|
}
|
|
|
|
return theTechCostsEnergy;
|
|
}
|
|
|
|
bool AvHSHUGetIsCombatModeTech(AvHMessageID inMessageID)
|
|
{
|
|
bool theIsCombatModeTech = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_SHOTGUN:
|
|
case BUILD_GRENADE_GUN:
|
|
case BUILD_HMG:
|
|
case BUILD_WELDER:
|
|
case BUILD_MINES:
|
|
case BUILD_JETPACK:
|
|
case BUILD_HEAVY:
|
|
case BUILD_SCAN:
|
|
theIsCombatModeTech = true;
|
|
break;
|
|
}
|
|
|
|
return theIsCombatModeTech;
|
|
}
|
|
|
|
bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID)
|
|
{
|
|
bool theIsResearchTech = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case RESEARCH_ELECTRICAL:
|
|
case RESEARCH_ARMOR_ONE:
|
|
case RESEARCH_ARMOR_TWO:
|
|
case RESEARCH_ARMOR_THREE:
|
|
case RESEARCH_WEAPONS_ONE:
|
|
case RESEARCH_WEAPONS_TWO:
|
|
case RESEARCH_WEAPONS_THREE:
|
|
case TURRET_FACTORY_UPGRADE:
|
|
case RESEARCH_JETPACKS:
|
|
case RESEARCH_HEAVYARMOR:
|
|
case RESEARCH_DISTRESSBEACON:
|
|
case RESEARCH_HEALTH:
|
|
case RESEARCH_CATALYSTS:
|
|
case MESSAGE_CANCEL:
|
|
case RESEARCH_MOTIONTRACK:
|
|
case RESEARCH_PHASETECH:
|
|
case RESEARCH_GRENADES:
|
|
|
|
case RESOURCE_UPGRADE:
|
|
case ARMORY_UPGRADE:
|
|
theIsResearchTech = true;
|
|
break;
|
|
}
|
|
|
|
return theIsResearchTech;
|
|
}
|
|
|
|
|
|
|
|
|
|
//Here's TFC's code that checks whether a player's allowed to build a sentry
|
|
//or not.
|
|
//I can't remember if there was any good reason why we used
|
|
//UTIL_FindEntitiesInSphere()
|
|
//instead of UTIL_EntitiesInBox().
|
|
//
|
|
////=========================================================================
|
|
//// Returns 0 if the area around obj is safe to build in
|
|
//int CBaseEntity::CheckArea( CBaseEntity *pIgnore )
|
|
//{
|
|
// TraceResult tr;
|
|
// Vector vecOrg = pev->origin;
|
|
//
|
|
// // Check the origin
|
|
// int iContents = UTIL_PointContents(vecOrg);
|
|
// if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER )
|
|
// return CAREA_BLOCKED;
|
|
//
|
|
// Vector vecIgnoreOrg = pIgnore->pev->origin;
|
|
// // Get the player's origin irrelevant of crouching
|
|
// if ( pIgnore->pev->flags & FL_DUCKING )
|
|
// {
|
|
// vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN -
|
|
// VEC_HULL_MIN);
|
|
// }
|
|
// // Trace a hull
|
|
// UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters,
|
|
// large_hull, edict(), &tr );
|
|
// CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
|
|
// if (tr.flFraction != 1 || tr.fAllSolid == 1)
|
|
// return CAREA_BLOCKED;
|
|
//
|
|
// // Check for solid entities in the area
|
|
// CBaseEntity *pEnt = NULL;
|
|
// while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) !=
|
|
// NULL )
|
|
// {
|
|
// // If it's not the engineer, and its a solid entity, fail
|
|
// if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid >
|
|
// SOLID_TRIGGER)
|
|
// return CAREA_BLOCKED;
|
|
// }
|
|
//
|
|
// // Cycle through all the Nobuild zones in the map and make sure this
|
|
// isn't in one of them
|
|
// CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL,
|
|
// "func_nobuild" );
|
|
// while ( pNoBuild )
|
|
// {
|
|
// // Check to see if we're building in this zone
|
|
// if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >=
|
|
// pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z &&
|
|
// vecOrg.x <= pNoBuild->pev->maxs.x &&
|
|
// vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z )
|
|
// return CAREA_NOBUILD;
|
|
//
|
|
// pNoBuild = UTIL_FindEntityByClassname( pNoBuild,
|
|
// "func_nobuild" );
|
|
// }
|
|
//
|
|
// // Check below
|
|
// UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64),
|
|
// dont_ignore_monsters, edict(), &tr );
|
|
// if ( tr.flFraction == 1.0 )
|
|
// return CAREA_BLOCKED;
|
|
//
|
|
// return CAREA_CLEAR;
|
|
//}
|
|
|
|
//bool AvHSHUGetIsEnoughRoom(const vec3_t& inCenter, const vec3_t& inMin, const vec3_t& inMax)
|
|
//{
|
|
// bool theEnoughRoom = false;
|
|
//
|
|
// // Do a traceline from center to center + min
|
|
// // Hit nothing?
|
|
// // Do a traceline from center to center + max
|
|
// // Hit nothing?
|
|
// // Success
|
|
// theEnoughRoom = true;
|
|
//
|
|
// return theEnoughRoom;
|
|
//}
|
|
|
|
bool AvHSHUGetIsGroupMessage(AvHMessageID inMessageID)
|
|
{
|
|
bool theIsGroupMessage = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case GROUP_CREATE_1:
|
|
case GROUP_CREATE_2:
|
|
case GROUP_CREATE_3:
|
|
case GROUP_CREATE_4:
|
|
case GROUP_CREATE_5:
|
|
case GROUP_SELECT_1:
|
|
case GROUP_SELECT_2:
|
|
case GROUP_SELECT_3:
|
|
case GROUP_SELECT_4:
|
|
case GROUP_SELECT_5:
|
|
theIsGroupMessage = true;
|
|
}
|
|
|
|
return theIsGroupMessage;
|
|
}
|
|
|
|
bool AvHSHUGetSizeForTech(AvHMessageID inMessageID, Vector& outMinSize, Vector& outMaxSize, bool inGetSizeToPlace)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Onos-sized
|
|
//const int theOnosHeightNeededToSpawn = HULL3_MAXZ - HULL3_MINZ + kRespawnFudgeFactorHeight;
|
|
//const int theOnosWidthNeededToSpawn = HULL3_MAXY - HULL3_MINY + kRespawnFudgeFactorHeight;
|
|
|
|
// Marine-sized
|
|
const int theMarineHeightNeededToSpawn = HULL0_MAXZ - HULL0_MINZ + kRespawnFudgeFactorHeight;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_INFANTRYPORTAL:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 14.49);
|
|
|
|
// Make sure there is room above it for players to spawn
|
|
if(inGetSizeToPlace)
|
|
{
|
|
outMaxSize.z += theMarineHeightNeededToSpawn;
|
|
}
|
|
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_PHASEGATE:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 14.49);
|
|
|
|
// Make sure there is remove above it for players to spawn
|
|
if(inGetSizeToPlace)
|
|
{
|
|
//outMinSize.x = outMinSize.y = -theOnosWidthNeededToSpawn;
|
|
//outMaxSize.x = outMaxSize.y = theOnosWidthNeededToSpawn;
|
|
//outMaxSize.z += theOnosHeightNeededToSpawn;
|
|
outMaxSize.z += theMarineHeightNeededToSpawn;
|
|
}
|
|
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_RESOURCES:
|
|
outMinSize = kResourceMinSize;
|
|
outMaxSize = kResourceMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case ALIEN_BUILD_RESOURCES:
|
|
outMinSize = kAlienResourceMinSize;
|
|
outMaxSize = kAlienResourceMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
default:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 66.9486);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_ARMSLAB:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 72.0 /*66.9486*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 42.0);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case ALIEN_BUILD_OFFENSE_CHAMBER:
|
|
case ALIEN_BUILD_DEFENSE_CHAMBER:
|
|
case ALIEN_BUILD_SENSORY_CHAMBER:
|
|
case ALIEN_BUILD_MOVEMENT_CHAMBER:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 44.0);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_COMMANDSTATION:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 73.0 /*70.34*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_TURRET_FACTORY:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
//outMaxSize = Vector(16.0, 16.0, 55.68);
|
|
outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_ARMORY:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_PROTOTYPE_LAB:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 73.0 /*67.7443*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_OBSERVATORY:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 80.7443);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
outMinSize = Vector(-16, -16, 0);
|
|
outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/ /*50.6678*/);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_HEALTH:
|
|
outMinSize = kHealthMinSize;
|
|
outMaxSize = kHealthMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_CAT:
|
|
outMinSize = kCatalystMinSize;
|
|
outMaxSize = kCatalystMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_HEAVY:
|
|
outMinSize = kHeavyMinSize;
|
|
outMaxSize = kHeavyMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_JETPACK:
|
|
outMinSize = kJetpackMinSize;
|
|
outMaxSize = kJetpackMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_AMMO:
|
|
outMinSize = kAmmoMinSize;
|
|
outMaxSize = kAmmoMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_MINES:
|
|
outMinSize = kWeaponMinSize;
|
|
outMaxSize = Vector(16, 16, 40);
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case BUILD_SHOTGUN:
|
|
case BUILD_HMG:
|
|
case BUILD_WELDER:
|
|
case BUILD_NUKE:
|
|
case BUILD_GRENADE_GUN:
|
|
outMinSize = kWeaponMinSize;
|
|
outMaxSize = kWeaponMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case ALIEN_BUILD_HIVE:
|
|
outMinSize = kHiveMinSize;
|
|
outMaxSize = kHiveMaxSize;
|
|
theSuccess = true;
|
|
break;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUGetSizeForPlayerUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize, bool inDucking)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Now set size
|
|
switch(inUser3)
|
|
{
|
|
case AVH_USER3_NONE:
|
|
case AVH_USER3_MARINE_PLAYER:
|
|
case AVH_USER3_COMMANDER_PLAYER:
|
|
case AVH_USER3_ALIEN_PLAYER4:
|
|
// Get simple case working first
|
|
if(inDucking)
|
|
{
|
|
outMinSize = HULL1_MIN;
|
|
outMaxSize = HULL1_MAX;
|
|
}
|
|
else
|
|
{
|
|
outMinSize = HULL0_MIN;
|
|
outMaxSize = HULL0_MAX;
|
|
}
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER1:
|
|
case AVH_USER3_ALIEN_PLAYER2:
|
|
case AVH_USER3_ALIEN_PLAYER3:
|
|
case AVH_USER3_ALIEN_EMBRYO:
|
|
outMinSize = HULL1_MIN;
|
|
outMaxSize = HULL1_MAX;
|
|
theSuccess = true;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER5:
|
|
if(inDucking)
|
|
{
|
|
outMinSize = HULL0_MIN;
|
|
outMaxSize = HULL0_MAX;
|
|
}
|
|
else
|
|
{
|
|
outMinSize = HULL3_MIN;
|
|
outMaxSize = HULL3_MAX;
|
|
}
|
|
theSuccess = true;
|
|
break;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUGetSizeForUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// If it's a player, get max size he can be (assuming he's not ducking)
|
|
theSuccess = AvHSHUGetSizeForPlayerUser3(inUser3, outMinSize, outMaxSize, false);
|
|
if(!theSuccess)
|
|
{
|
|
AvHMessageID theMessageID = MESSAGE_NULL;
|
|
|
|
// Convert it to a AvHMessageID if possible
|
|
if(AvHSHUUser3ToMessageID(inUser3, theMessageID))
|
|
{
|
|
theSuccess = AvHSHUGetSizeForTech(theMessageID, outMinSize, outMaxSize);
|
|
}
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUUser3ToMessageID(AvHUser3 inUser3, AvHMessageID& outMessageID)
|
|
{
|
|
AvHMessageID theMessageID = MESSAGE_NULL;
|
|
bool theSuccess = false;
|
|
|
|
switch(inUser3)
|
|
{
|
|
case AVH_USER3_FUNC_RESOURCE:
|
|
case AVH_USER3_RESTOWER:
|
|
theMessageID = BUILD_RESOURCES;
|
|
break;
|
|
|
|
case AVH_USER3_COMMANDER_STATION:
|
|
theMessageID = BUILD_COMMANDSTATION;
|
|
break;
|
|
|
|
case AVH_USER3_TURRET_FACTORY:
|
|
case AVH_USER3_ADVANCED_TURRET_FACTORY:
|
|
theMessageID = BUILD_TURRET_FACTORY;
|
|
break;
|
|
|
|
case AVH_USER3_ARMORY:
|
|
case AVH_USER3_ADVANCED_ARMORY:
|
|
theMessageID = BUILD_ARMORY;
|
|
break;
|
|
|
|
case AVH_USER3_ARMSLAB:
|
|
theMessageID = BUILD_ARMSLAB;
|
|
break;
|
|
|
|
case AVH_USER3_PROTOTYPE_LAB:
|
|
theMessageID = BUILD_PROTOTYPE_LAB;
|
|
break;
|
|
|
|
case AVH_USER3_OBSERVATORY:
|
|
theMessageID = BUILD_OBSERVATORY;
|
|
break;
|
|
|
|
case AVH_USER3_TURRET:
|
|
theMessageID = BUILD_TURRET;
|
|
break;
|
|
|
|
case AVH_USER3_SIEGETURRET:
|
|
theMessageID = BUILD_SIEGE;
|
|
break;
|
|
|
|
case AVH_USER3_INFANTRYPORTAL:
|
|
theMessageID = BUILD_INFANTRYPORTAL;
|
|
break;
|
|
|
|
case AVH_USER3_PHASEGATE:
|
|
theMessageID = BUILD_PHASEGATE;
|
|
break;
|
|
|
|
case AVH_USER3_HEAVY:
|
|
theMessageID = BUILD_HEAVY;
|
|
break;
|
|
|
|
case AVH_USER3_JETPACK:
|
|
theMessageID = BUILD_JETPACK;
|
|
break;
|
|
|
|
case AVH_USER3_DEFENSE_CHAMBER:
|
|
theMessageID = ALIEN_BUILD_DEFENSE_CHAMBER;
|
|
break;
|
|
|
|
case AVH_USER3_MOVEMENT_CHAMBER:
|
|
theMessageID = ALIEN_BUILD_MOVEMENT_CHAMBER;
|
|
break;
|
|
|
|
case AVH_USER3_OFFENSE_CHAMBER:
|
|
theMessageID = ALIEN_BUILD_OFFENSE_CHAMBER;
|
|
break;
|
|
|
|
case AVH_USER3_SENSORY_CHAMBER:
|
|
theMessageID = ALIEN_BUILD_SENSORY_CHAMBER;
|
|
break;
|
|
|
|
case AVH_USER3_ALIENRESTOWER:
|
|
theMessageID = ALIEN_BUILD_RESOURCES;
|
|
break;
|
|
|
|
case AVH_USER3_HIVE:
|
|
theMessageID = ALIEN_BUILD_HIVE;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER1:
|
|
theMessageID = ALIEN_LIFEFORM_ONE;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER2:
|
|
theMessageID = ALIEN_LIFEFORM_TWO;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER3:
|
|
theMessageID = ALIEN_LIFEFORM_THREE;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER4:
|
|
theMessageID = ALIEN_LIFEFORM_FOUR;
|
|
break;
|
|
|
|
case AVH_USER3_ALIEN_PLAYER5:
|
|
theMessageID = ALIEN_LIFEFORM_FIVE;
|
|
break;
|
|
}
|
|
|
|
if(theMessageID != MESSAGE_NULL)
|
|
{
|
|
outMessageID = theMessageID;
|
|
theSuccess = true;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUMessageIDToUser3(AvHMessageID inMessageID, AvHUser3& outUser3)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
AvHUser3 theUser3 = AVH_USER3_NONE;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_RESOURCES:
|
|
theUser3 = AVH_USER3_RESTOWER;
|
|
break;
|
|
|
|
case BUILD_COMMANDSTATION:
|
|
theUser3 = AVH_USER3_COMMANDER_STATION;
|
|
break;
|
|
|
|
case BUILD_TURRET_FACTORY:
|
|
theUser3 = AVH_USER3_TURRET_FACTORY;
|
|
break;
|
|
|
|
case TURRET_FACTORY_UPGRADE:
|
|
theUser3 = AVH_USER3_ADVANCED_TURRET_FACTORY;
|
|
break;
|
|
|
|
case BUILD_ARMORY:
|
|
theUser3 = AVH_USER3_ARMORY;
|
|
break;
|
|
|
|
case ARMORY_UPGRADE:
|
|
theUser3 = AVH_USER3_ADVANCED_ARMORY;
|
|
break;
|
|
|
|
case BUILD_ARMSLAB:
|
|
theUser3 = AVH_USER3_ARMSLAB;
|
|
break;
|
|
|
|
case BUILD_PROTOTYPE_LAB:
|
|
theUser3 = AVH_USER3_PROTOTYPE_LAB;
|
|
break;
|
|
|
|
case BUILD_OBSERVATORY:
|
|
theUser3 = AVH_USER3_OBSERVATORY;
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
theUser3 = AVH_USER3_TURRET;
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
theUser3 = AVH_USER3_SIEGETURRET;
|
|
break;
|
|
|
|
case BUILD_INFANTRYPORTAL:
|
|
theUser3 = AVH_USER3_INFANTRYPORTAL;
|
|
break;
|
|
|
|
case BUILD_PHASEGATE:
|
|
theUser3 = AVH_USER3_PHASEGATE;
|
|
break;
|
|
|
|
case BUILD_HEAVY:
|
|
theUser3 = AVH_USER3_HEAVY;
|
|
break;
|
|
|
|
case BUILD_JETPACK:
|
|
theUser3 = AVH_USER3_JETPACK;
|
|
break;
|
|
|
|
// Menus
|
|
case MENU_BUILD:
|
|
theUser3 = AVH_USER3_MENU_BUILD;
|
|
break;
|
|
case MENU_BUILD_ADVANCED:
|
|
theUser3 = AVH_USER3_MENU_BUILD_ADVANCED;
|
|
break;
|
|
case MENU_ASSIST:
|
|
theUser3 = AVH_USER3_MENU_ASSIST;
|
|
break;
|
|
case MENU_EQUIP:
|
|
theUser3 = AVH_USER3_MENU_EQUIP;
|
|
break;
|
|
|
|
// Weapons
|
|
case BUILD_MINES:
|
|
theUser3 = AVH_USER3_MINE;
|
|
break;
|
|
|
|
// Lifeforms
|
|
case ALIEN_LIFEFORM_ONE:
|
|
theUser3 = AVH_USER3_ALIEN_PLAYER1;
|
|
break;
|
|
|
|
case ALIEN_LIFEFORM_TWO:
|
|
theUser3 = AVH_USER3_ALIEN_PLAYER2;
|
|
break;
|
|
|
|
case ALIEN_LIFEFORM_THREE:
|
|
theUser3 = AVH_USER3_ALIEN_PLAYER3;
|
|
break;
|
|
|
|
case ALIEN_LIFEFORM_FOUR:
|
|
theUser3 = AVH_USER3_ALIEN_PLAYER4;
|
|
break;
|
|
|
|
case ALIEN_LIFEFORM_FIVE:
|
|
theUser3 = AVH_USER3_ALIEN_PLAYER5;
|
|
break;
|
|
}
|
|
|
|
if(theUser3 != AVH_USER3_NONE)
|
|
{
|
|
outUser3 = theUser3;
|
|
theSuccess = true;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
|
|
float AvHSHUGetTime()
|
|
{
|
|
float theTime = 0;
|
|
|
|
#ifdef AVH_SERVER
|
|
theTime = gpGlobals->time;
|
|
#else
|
|
theTime = gEngfuncs.GetClientTime();
|
|
#endif
|
|
|
|
return theTime;
|
|
}
|
|
|
|
|
|
|
|
// Note, all these models must be precached already
|
|
char* AvHSHUGetBuildTechModelName(AvHMessageID inMessageID)
|
|
{
|
|
char* theModelName = NULL;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_RESOURCES:
|
|
theModelName = kResourceTowerModel;
|
|
break;
|
|
|
|
//case BUILD_REINFORCEMENTS:
|
|
// theModelName = kInfantryPortalModel;
|
|
// break;
|
|
|
|
case BUILD_INFANTRYPORTAL:
|
|
theModelName = kInfantryPortalModel;
|
|
break;
|
|
|
|
case BUILD_COMMANDSTATION:
|
|
theModelName = kCommandStationModel;
|
|
break;
|
|
|
|
case BUILD_TURRET_FACTORY:
|
|
theModelName = kTurretFactoryModel;
|
|
break;
|
|
|
|
case BUILD_ARMSLAB:
|
|
theModelName = kArmsLabModel;
|
|
break;
|
|
|
|
case BUILD_PROTOTYPE_LAB:
|
|
theModelName = kPrototypeLabModel;
|
|
break;
|
|
|
|
case BUILD_ARMORY:
|
|
theModelName = kArmoryModel;
|
|
break;
|
|
|
|
case ARMORY_UPGRADE:
|
|
theModelName = kAdvancedWeaponFactoryModel;
|
|
break;
|
|
|
|
case BUILD_OBSERVATORY:
|
|
theModelName = kObservatoryModel;
|
|
break;
|
|
|
|
case BUILD_SCAN:
|
|
theModelName = kScanModel;
|
|
break;
|
|
|
|
case BUILD_PHASEGATE:
|
|
theModelName = kPhaseGateModel;
|
|
break;
|
|
|
|
case BUILD_TURRET:
|
|
theModelName = kDeployedTurretModel;
|
|
break;
|
|
|
|
case BUILD_NUKE:
|
|
theModelName = kNukeModel;
|
|
break;
|
|
|
|
case BUILD_SIEGE:
|
|
theModelName = kSiegeTurretModel;
|
|
//theModelName = kDeployedTurretModel;
|
|
break;
|
|
|
|
case BUILD_CAT:
|
|
theModelName = kCatalystModel;
|
|
break;
|
|
|
|
case BUILD_HEALTH:
|
|
theModelName = kHealthModel;
|
|
break;
|
|
|
|
case BUILD_HEAVY:
|
|
theModelName = kHeavyModel;
|
|
break;
|
|
|
|
case BUILD_JETPACK:
|
|
theModelName = kJetpackModel;
|
|
break;
|
|
|
|
case BUILD_AMMO:
|
|
theModelName = kAmmoModel;
|
|
break;
|
|
|
|
case BUILD_WELDER:
|
|
theModelName = kWelderWModel;
|
|
break;
|
|
|
|
case BUILD_MINES:
|
|
theModelName = kTripmineW2Model;
|
|
break;
|
|
|
|
case BUILD_SHOTGUN:
|
|
theModelName = kSGWModel;
|
|
break;
|
|
|
|
case BUILD_HMG:
|
|
theModelName = kHMGWModel;
|
|
break;
|
|
|
|
case BUILD_GRENADE_GUN:
|
|
theModelName = kGGWModel;
|
|
break;
|
|
|
|
// Alien buildings
|
|
case ALIEN_BUILD_HIVE:
|
|
theModelName = kHiveModel;
|
|
break;
|
|
|
|
case ALIEN_BUILD_RESOURCES:
|
|
theModelName = kAlienResourceTowerModel;
|
|
break;
|
|
|
|
case ALIEN_BUILD_OFFENSE_CHAMBER:
|
|
theModelName = kOffenseChamberModel;
|
|
break;
|
|
|
|
case ALIEN_BUILD_DEFENSE_CHAMBER:
|
|
theModelName = kDefenseChamberModel;
|
|
break;
|
|
|
|
case ALIEN_BUILD_SENSORY_CHAMBER:
|
|
theModelName = kSensoryChamberModel;
|
|
break;
|
|
|
|
case ALIEN_BUILD_MOVEMENT_CHAMBER:
|
|
theModelName = kMovementChamberModel;
|
|
break;
|
|
}
|
|
|
|
return theModelName;
|
|
}
|
|
|
|
bool AvHSHUGetBuildTechRange(AvHMessageID inMessageID, float& outRange)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// switch(inMessageID)
|
|
// {
|
|
// case BUILD_CAMERA:
|
|
// outRange = kResourceTowerSightRange;
|
|
// theSuccess = true;
|
|
// break;
|
|
// case BUILD_PHASEGATE:
|
|
// break;
|
|
// case BUILD_TURRET:
|
|
// outRange = TURRET_RANGE;
|
|
// theSuccess = true;
|
|
// break;
|
|
// case BUILD_SIEGE:
|
|
// outRange = kSiegeTurretMaxRange;
|
|
// theSuccess = true;
|
|
// break;
|
|
// }
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUTraceLineIsAreaFree(Vector& inStart, Vector& inEnd, edict_t* inIgnoreEntity, bool inIgnorePlayers)
|
|
{
|
|
bool theAreaIsFree = true;
|
|
|
|
#ifdef AVH_SERVER
|
|
TraceResult theTR;
|
|
bool theIsDone = false;
|
|
|
|
// Do tracelines between the corners, to make sure there's no geometry inside the box
|
|
int theNumIters = 0;
|
|
|
|
IGNORE_MONSTERS theIgnoreMonsters = dont_ignore_monsters;
|
|
|
|
if (inIgnorePlayers)
|
|
{
|
|
theIgnoreMonsters = ignore_monsters;
|
|
}
|
|
|
|
while(!theIsDone)
|
|
{
|
|
UTIL_TraceLine(inStart, inEnd, theIgnoreMonsters, inIgnoreEntity, &theTR);
|
|
if(theTR.flFraction != 1.0f)
|
|
{
|
|
CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theTR.pHit));
|
|
if(theEntity && (theEntity->pev->solid != SOLID_BBOX) && (theEntity->pev->solid != SOLID_SLIDEBOX) && (theEntity->pev->solid != SOLID_BSP) && (inIgnorePlayers != !!theEntity->IsPlayer()) )
|
|
{
|
|
VectorCopy(theTR.vecEndPos, inStart);
|
|
}
|
|
else
|
|
{
|
|
theIsDone = true;
|
|
theAreaIsFree = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theIsDone = true;
|
|
}
|
|
|
|
if(theNumIters++ > 50)
|
|
{
|
|
theIsDone = true;
|
|
theAreaIsFree = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// int theIndex;
|
|
// vec3_t theEndLocation;
|
|
// AvHTeamNumber theTeam;
|
|
// bool thePlayerHit;
|
|
// int theUserThree;
|
|
// int theUserFour;
|
|
//
|
|
// if(AvHSHUTraceTangible(inStart, inEnd, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour))
|
|
// {
|
|
// if(theIndex >= 0)
|
|
// {
|
|
// theAreaIsFree = false;
|
|
// }
|
|
// }
|
|
|
|
return theAreaIsFree;
|
|
}
|
|
|
|
float AvHTraceLineAgainstWorld(Vector& vecStart, Vector& vecEnd)
|
|
{
|
|
|
|
#ifdef AVH_SERVER
|
|
|
|
TraceResult tr;
|
|
UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, dont_ignore_glass, NULL, &tr);
|
|
|
|
return tr.flFraction;
|
|
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
|
|
// This may not be the most efficient way, but it seems to get the job done.
|
|
|
|
float theFraction = 1;
|
|
|
|
for (int i = 0; i < kMaxEntities && theFraction > 0; ++i)
|
|
{
|
|
|
|
cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(i);
|
|
|
|
if (theEntity != NULL && theEntity->model != NULL && theEntity->model->type == mod_brush)
|
|
{
|
|
|
|
vec3_t localStart;
|
|
vec3_t localEnd;
|
|
|
|
// Translate the start and end into the model's frame of reference.
|
|
|
|
VectorSubtract(vecStart, theEntity->origin, localStart);
|
|
VectorSubtract(vecEnd, theEntity->origin, localEnd);
|
|
|
|
// Rotate the start and end into the model's frame of reference.
|
|
|
|
if (theEntity->angles[0] ||
|
|
theEntity->angles[1] ||
|
|
theEntity->angles[2])
|
|
{
|
|
|
|
vec3_t forward;
|
|
vec3_t right;
|
|
vec3_t up;
|
|
|
|
AngleVectors(theEntity->angles, forward, right, up);
|
|
|
|
vec3_t temp;
|
|
|
|
VectorCopy(localStart, temp);
|
|
localStart[0] = DotProduct(temp, forward);
|
|
localStart[1] = -DotProduct(temp, right);
|
|
localStart[2] = DotProduct(temp, up);
|
|
|
|
VectorCopy(localEnd, temp);
|
|
localEnd[0] = DotProduct(temp, forward);
|
|
localEnd[1] = -DotProduct(temp, right);
|
|
localEnd[2] = DotProduct(temp, up);
|
|
|
|
}
|
|
|
|
trace_t tr;
|
|
NS_TraceLine(&theEntity->model->hulls[0], localStart, localEnd, &tr);
|
|
|
|
if (tr.fraction < theFraction)
|
|
{
|
|
theFraction = tr.fraction;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return theFraction;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
bool AvHSHUGetCanBeBuiltOnPlayers(AvHMessageID inMessageID)
|
|
{
|
|
bool theCanBeBuiltOnPlayers = false;
|
|
|
|
switch(inMessageID)
|
|
{
|
|
case ALIEN_BUILD_HIVE:
|
|
case BUILD_SCAN:
|
|
case BUILD_AMMO:
|
|
case BUILD_HEALTH:
|
|
case BUILD_CAT:
|
|
case BUILD_MINES:
|
|
case BUILD_WELDER:
|
|
case BUILD_SHOTGUN:
|
|
case BUILD_HMG:
|
|
case BUILD_GRENADE_GUN:
|
|
case BUILD_HEAVY:
|
|
case BUILD_JETPACK:
|
|
theCanBeBuiltOnPlayers = true;
|
|
break;
|
|
}
|
|
|
|
return theCanBeBuiltOnPlayers;
|
|
}
|
|
|
|
bool AvHSHUGetIsSiteValidForBuild(AvHMessageID inMessageID, Vector* inLocation, int inIgnoredEntityIndex)
|
|
{
|
|
//TODO: - check entity returned by client side commander trace for drop validity before we ever get this far
|
|
|
|
bool theSuccess = false;
|
|
|
|
// Check that there's enough room for building
|
|
vec3_t theMinSize, theMaxSize;
|
|
if(AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize, true))
|
|
{
|
|
|
|
// If it's a build with special placement requirements, check that here (this could modify the location also)
|
|
if(AvHSHUGetAreSpecialBuildingRequirementsMet(inMessageID, *inLocation))
|
|
{
|
|
|
|
// Assume mappers place hives correctly (it's such a showstopper if aliens can't create a hive because it's too close to a wall)
|
|
// KGP 4/28/04 - add res towers to hives for the same reason -- if a mapper didn't give room, it's a showstopper.
|
|
bool theIgnorePlayers = AvHSHUGetCanBeBuiltOnPlayers(inMessageID);
|
|
float theSlopeTangent = theIgnorePlayers ? kMaxEquipmentDropSlope : kMaxBuildingDropSlope;
|
|
bool theSkipDropCheck = false;
|
|
switch(inMessageID)
|
|
{
|
|
case BUILD_RESOURCES:
|
|
theSkipDropCheck = true;
|
|
break;
|
|
case ALIEN_BUILD_RESOURCES:
|
|
theSkipDropCheck = true;
|
|
break;
|
|
case ALIEN_BUILD_HIVE:
|
|
theSkipDropCheck = true;
|
|
break;
|
|
}
|
|
if(theSkipDropCheck || AvHSHUGetCanDropItem(*inLocation, theMinSize, theMaxSize, theSlopeTangent, inIgnoredEntityIndex, theIgnorePlayers))
|
|
{
|
|
//TODO: - better check for entity below drop, since it's often off center.
|
|
// Check to make sure building isn't being created on top of another building
|
|
vec3_t thePositionBelowBuild = *inLocation;
|
|
thePositionBelowBuild.z = -kMaxMapDimension;
|
|
|
|
int theIndex;
|
|
vec3_t theEndLocation;
|
|
AvHTeamNumber theTeam;
|
|
bool thePlayerHit;
|
|
int theUserThree;
|
|
int theUserFour;
|
|
|
|
// Don't allow building on any entities, except allow resource towers on top of func_resources
|
|
|
|
//TODO : use full list instead of physents, which isn't accounting for items the commander can't see
|
|
bool theHitSomething = AvHSHUTraceTangible(*inLocation, thePositionBelowBuild, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour);
|
|
|
|
//res building case
|
|
if(inMessageID == BUILD_RESOURCES || inMessageID == ALIEN_BUILD_RESOURCES)
|
|
{
|
|
#ifdef AVH_SERVER //on server, return false if occupied
|
|
FOR_ALL_ENTITIES(kesFuncResource,AvHFuncResource*)
|
|
if(!theEntity->GetIsOccupied()) // open for use
|
|
{
|
|
if(VectorDistance(theEntity->pev->origin,*inLocation) < 20) //small enough that we don't check the wrong nozzle
|
|
{
|
|
theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE);
|
|
break;
|
|
}
|
|
}
|
|
END_FOR_ALL_ENTITIES(kesFuncResource)
|
|
#else //on client the occupied function isn't available
|
|
theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE);
|
|
#endif
|
|
}
|
|
else if ( inMessageID == ALIEN_BUILD_HIVE )
|
|
{
|
|
theSuccess = true;
|
|
//theSuccess = ( !theHitSomething || ( *inLocation[2] - theEndLocation[2] > 40.0f ) );
|
|
//#ifdef AVH_SERVER
|
|
// ALERT(at_console, "theHitSomething=%d theSuccess=%d\n", theHitSomething, theSuccess);
|
|
// ALERT(at_console, "%f\n", *inLocation[2] - theEndLocation[2]);
|
|
// ALERT(at_console, "{%f, %f, %f} : { %f, %f, %f }\n",
|
|
// inLocation[0], inLocation[1], inLocation[2],
|
|
// theEndLocation[0], theEndLocation[1], theEndLocation[2]);
|
|
//#endif
|
|
}
|
|
else if(!theHitSomething || (theIgnorePlayers && thePlayerHit) || theIndex <= 0)
|
|
{
|
|
// THEN it's a legal build spot and the building shows up green.
|
|
theSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return theSuccess;
|
|
}
|
|
|
|
int AvHSHUGetPointContents(vec3_t inPoint)
|
|
{
|
|
int thePointContents = 0;
|
|
|
|
#ifdef AVH_SERVER
|
|
thePointContents = UTIL_PointContents(inPoint);
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1);
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
|
|
thePointContents = gEngfuncs.PM_PointContents(inPoint, NULL);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
#endif
|
|
|
|
return thePointContents;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the build or item is overlapping the world.
|
|
*/
|
|
bool AvHSHUGetIsVolumeContentNonsolid(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
|
|
{
|
|
|
|
float theMins[3], theMaxs[3];
|
|
VectorAdd(inCenter,inMinSize,theMins);
|
|
VectorAdd(inCenter,inMaxSize,theMaxs);
|
|
|
|
bool theIsNonsolid = true; //innocent until proven guilty
|
|
|
|
extern playermove_t *pmove;
|
|
if (pmove != NULL && pmove->physents[0].model != NULL)
|
|
{
|
|
const hull_t* theHull = &pmove->physents[0].model->hulls[NS_GetValveHull(2)];
|
|
int theContents = NS_BoxContents(theHull,theHull->firstclipnode,theMins,theMaxs);
|
|
theIsNonsolid = (theContents != CONTENT_SOLID);
|
|
}
|
|
else //fallback if we're just starting a round
|
|
{
|
|
int theContents = AvHSHUGetPointContents(inCenter);
|
|
if(theContents != CONTENT_SOLID)
|
|
{
|
|
//trace between each corner pair once looking for obstructions - 28 total comparisons
|
|
const static int MIN = 0;
|
|
const static int MAX = 1;
|
|
const static int NUM_CORNERS = 8;
|
|
int theIndex;
|
|
Vector theCorners[NUM_CORNERS];
|
|
|
|
for(int XPos = MIN; XPos <= MAX; ++XPos)
|
|
{
|
|
for(int YPos = MIN; YPos <= MAX; ++YPos)
|
|
{
|
|
for(int ZPos = MIN; ZPos <= MAX; ++ZPos)
|
|
{
|
|
theIndex = XPos+YPos*2+ZPos*4;
|
|
theCorners[theIndex].x = inCenter.x + ((XPos == MIN) ? inMinSize.x : inMaxSize.x);
|
|
theCorners[theIndex].y = inCenter.y + ((YPos == MIN) ? inMinSize.y : inMaxSize.y);
|
|
theCorners[theIndex].z = inCenter.z + ((ZPos == MIN) ? inMinSize.z : inMaxSize.z);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int startCorner = 0; startCorner < NUM_CORNERS; ++startCorner)
|
|
{
|
|
for(int endCorner = startCorner+1; endCorner < NUM_CORNERS; ++endCorner)
|
|
{
|
|
if(!AvHSHUTraceLineIsAreaFree(theCorners[startCorner],theCorners[endCorner],inIgnoreEntity,inIgnorePlayers))
|
|
{
|
|
theIsNonsolid = false;
|
|
break;
|
|
}
|
|
}
|
|
if(!theIsNonsolid)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theIsNonsolid = false;
|
|
}
|
|
}
|
|
return theIsNonsolid;
|
|
}
|
|
|
|
bool AvHSHUGetIsAreaFree(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
|
|
{
|
|
bool theAreaIsFree = AvHSHUGetIsVolumeContentNonsolid(inCenter,inMinSize,inMaxSize,inIgnoreEntity,inIgnorePlayers);
|
|
|
|
if(theAreaIsFree)
|
|
{
|
|
theAreaIsFree = !AvHSHUGetEntitiesBlocking(inCenter, inMinSize, inMaxSize, inIgnoreEntity, inIgnorePlayers);
|
|
}
|
|
|
|
return theAreaIsFree;
|
|
}
|
|
|
|
bool AvHSHUGetEntitiesBlocking(Vector& inOrigin, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
|
|
{
|
|
bool theEntBlocking = false;
|
|
|
|
typedef vector< pair<int, Vector> > PhysEntListType;
|
|
PhysEntListType theEntList;
|
|
|
|
const int kSearchRadius = 800;
|
|
|
|
// Populate phys ent list
|
|
#ifdef AVH_SERVER
|
|
CBaseEntity* theEntity = NULL;
|
|
while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, kSearchRadius)) != NULL)
|
|
{
|
|
|
|
// If entity is visible non-world object, add it
|
|
if((theEntity->pev->team != TEAM_IND) && (theEntity->pev->solid != SOLID_NOT && theEntity->pev->solid != SOLID_TRIGGER))
|
|
{
|
|
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntity);
|
|
|
|
if(theEntity->edict() != inIgnoreEntity && !(thePlayer != NULL && inIgnorePlayers))
|
|
{
|
|
theEntList.push_back(make_pair(theEntity->pev->iuser3, theEntity->pev->origin));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
// Haven't implemented this, so depend on it not being passed in
|
|
ASSERT(inIgnoreEntity == NULL);
|
|
|
|
for(int i = 0; i < pmove->numphysent; i++)
|
|
{
|
|
physent_t* thePhysEnt = pmove->physents + i;
|
|
if((thePhysEnt->team != TEAM_IND) && (thePhysEnt->solid != SOLID_NOT && thePhysEnt->solid != SOLID_TRIGGER))
|
|
{
|
|
theEntList.push_back(make_pair(thePhysEnt->iuser3, thePhysEnt->origin));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Vector theAbsMax;
|
|
VectorAdd(inOrigin, inMaxSize, theAbsMax);
|
|
|
|
Vector theAbsMin;
|
|
VectorAdd(inOrigin, inMinSize, theAbsMin);
|
|
|
|
Vector theOrigin1;
|
|
VectorAdd(theAbsMax, theAbsMin, theOrigin1);
|
|
theOrigin1 = theOrigin1*.5f;
|
|
|
|
Vector theSize1;
|
|
VectorSubtract(theAbsMax, theAbsMin, theSize1);
|
|
theSize1 = theSize1*.5f;
|
|
|
|
// Do collision on list, making sure none are too close to inOrigin
|
|
for(PhysEntListType::iterator theIter = theEntList.begin(); theIter != theEntList.end(); theIter++)
|
|
{
|
|
// Get size for tech
|
|
Vector theMinSize, theMaxSize;
|
|
if(AvHSHUGetSizeForUser3(AvHUser3(theIter->first), theMinSize, theMaxSize))
|
|
{
|
|
Vector theEntOrigin = theIter->second;
|
|
|
|
Vector theAbsMax;
|
|
VectorAdd(theEntOrigin, theMaxSize, theAbsMax);
|
|
|
|
Vector theAbsMin;
|
|
VectorAdd(theEntOrigin, inMinSize, theAbsMin);
|
|
|
|
Vector theOrigin2;
|
|
VectorAdd(theAbsMax, theAbsMin, theOrigin2);
|
|
theOrigin2 = theOrigin2*.5f;
|
|
|
|
Vector theSize2;
|
|
VectorSubtract(theAbsMax, theAbsMin, theSize2);
|
|
theSize2 = theSize2*.5f;
|
|
|
|
// Do simple box collision here
|
|
if(NS_BoxesOverlap((float*)theOrigin1, (float*)theSize1, (float*)theOrigin2, (float*)theSize2))
|
|
{
|
|
theEntBlocking = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return theEntBlocking;
|
|
}
|
|
|
|
|
|
|
|
void AvHSHUMakeViewFriendlyKillerName(string& ioKillerName)
|
|
{
|
|
string theOutputName = ioKillerName;
|
|
int theStrLen = (int)ioKillerName.length();
|
|
|
|
if(!strncmp(ioKillerName.c_str(), "weapon_", 7))
|
|
{
|
|
theOutputName = ioKillerName.substr(7);
|
|
}
|
|
else if(!strncmp(ioKillerName.c_str(), "monster_", 8))
|
|
{
|
|
theOutputName = ioKillerName.substr(8);
|
|
}
|
|
else if(!strncmp(ioKillerName.c_str(), "func_", 5))
|
|
{
|
|
theOutputName = ioKillerName.substr(5);
|
|
}
|
|
|
|
ioKillerName = theOutputName;
|
|
}
|
|
|
|
bool AvHSHUGetCanDropItem(vec3_t& ioCenter, Vector& inMinSize, Vector& inMaxSize, float inMaxSlopeTangent, int inIgnoreIndex, bool inIgnorePlayers)
|
|
{
|
|
|
|
float theMaxXWidth = max(-inMinSize[0],inMaxSize[0]);
|
|
float theMaxYWidth = max(-inMinSize[1],inMaxSize[1]);
|
|
float theRadius = sqrt(theMaxXWidth*theMaxXWidth + theMaxYWidth * theMaxYWidth);
|
|
float theHeight = inMaxSize[2] - inMinSize[2];
|
|
//adjust origin to be base
|
|
float theOrigin[3] = { ioCenter[0], ioCenter[1], ioCenter[2] + inMinSize[2] };
|
|
|
|
CollisionChecker Checker(pmove, kHLPointHullIndex, CollisionChecker::HULL_TYPE_ALL, inIgnorePlayers, CollisionChecker::IGNORE_NONE, inIgnoreIndex);
|
|
bool theCanDropItem = (Checker.GetContentsInCylinder(theOrigin,theRadius,theHeight) != CONTENTS_SOLID);
|
|
|
|
|
|
if(!theCanDropItem) //can't place it -- can we drop it?
|
|
{
|
|
|
|
float theMaxDropHeight = theRadius * inMaxSlopeTangent;
|
|
|
|
float theDropOrigin[3] = { theOrigin[0], theOrigin[1], theOrigin[2] + theMaxDropHeight };
|
|
theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID);
|
|
|
|
if(theCanDropItem) //can drop it -- get as low to ground as possible for drop
|
|
{
|
|
|
|
float theBestDropHeight = theMaxDropHeight;
|
|
float theShiftFraction = 1.0f;
|
|
for(float theShiftAdjust = 0.5f; theShiftAdjust > 0.05f; theShiftAdjust /= 2)
|
|
{
|
|
theShiftFraction += theShiftAdjust * (theCanDropItem ? -1 : 1); //try lower if last was a success
|
|
theDropOrigin[2] = theMaxDropHeight;
|
|
theDropOrigin[2] *= theShiftFraction;
|
|
theDropOrigin[2] += theOrigin[2];
|
|
|
|
theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID);
|
|
if(theCanDropItem)
|
|
{
|
|
theBestDropHeight = theShiftFraction;
|
|
theBestDropHeight *= theMaxDropHeight;
|
|
}
|
|
}
|
|
|
|
//adjust center to best position
|
|
ioCenter[2] += theBestDropHeight;
|
|
theCanDropItem = true;
|
|
}
|
|
}
|
|
|
|
return theCanDropItem;
|
|
}
|
|
|
|
bool AvHSHUTraceAndGetIsSiteValidForBuild(AvHMessageID inMessageID, const Vector& inPointOfView, const Vector& inNormRay, Vector* outLocation)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// TODO: Check if area is within the mapextents
|
|
|
|
int theUser3;
|
|
bool theTraceSuccess = AvHSHUTraceTangible(inPointOfView, inNormRay, &theUser3, outLocation);
|
|
|
|
// : 0000291
|
|
// ignore trace for scans (removed due to cost being paid when drop failed)
|
|
//if (inMessageID == BUILD_SCAN)
|
|
//{
|
|
// theSuccess = true;
|
|
//}
|
|
//else
|
|
// :
|
|
if(theTraceSuccess)
|
|
{
|
|
// : 0000291
|
|
if((inMessageID == BUILD_SCAN) || (AvHSHUGetIsSiteValidForBuild(inMessageID, outLocation)))
|
|
// :
|
|
{
|
|
theSuccess = true;
|
|
}
|
|
|
|
}
|
|
return theSuccess;
|
|
}
|
|
|
|
#ifdef AVH_CLIENT
|
|
bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Offset starting position a little so we don't select ourselves
|
|
Vector theStartPosition;
|
|
VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPosition);
|
|
|
|
Vector theEndPos;
|
|
VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
int theNumPlayers = gEngfuncs.GetMaxClients();
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 );
|
|
|
|
bool theDone = false;
|
|
int theEntityIndex = -1;
|
|
|
|
vec3_t theNewStartPos;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPosition, theEndPos, theNewStartPos);
|
|
VectorCopy(theNewStartPos, theStartPosition);
|
|
|
|
do
|
|
{
|
|
pmtrace_t tr;
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPosition, theEndPos, PM_NORMAL, theEntityIndex, &tr );
|
|
|
|
physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
|
|
theEntityIndex = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );
|
|
|
|
if(pEntity)
|
|
{
|
|
int theUser3 = pEntity->iuser3;
|
|
if(theEntityIndex > 0)
|
|
{
|
|
outEntIndex = theEntityIndex;
|
|
theSuccess = true;
|
|
theDone = true;
|
|
}
|
|
}
|
|
|
|
if((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction < kFloatTolerance))
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(tr.endpos, theStartPosition);
|
|
}
|
|
|
|
} while(!theDone);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
return theSuccess;
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex)
|
|
{
|
|
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;
|
|
|
|
vec3_t theNewStartPos;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
|
|
VectorCopy(theNewStartPos, theStartPos);
|
|
|
|
do
|
|
{
|
|
UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, ignore_glass, theEdictToIgnore, &tr);
|
|
|
|
theEntityHit = CBaseEntity::Instance(tr.pHit);
|
|
if(theEntityHit)
|
|
{
|
|
if(theEntityHit->entindex() > 0)
|
|
{
|
|
outEntIndex = theEntityHit->entindex();
|
|
theSuccess = true;
|
|
theDone = true;
|
|
}
|
|
}
|
|
|
|
|
|
if((tr.flFraction > (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance))
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
if(theEntityHit)
|
|
{
|
|
theEdictToIgnore = ENT(theEntityHit->pev);
|
|
}
|
|
VectorCopy(tr.vecEndPos, theStartPos);
|
|
}
|
|
} while(!theDone);
|
|
|
|
return theSuccess;
|
|
}
|
|
#endif
|
|
|
|
const AvHMapExtents& AvHSHUGetMapExtents()
|
|
{
|
|
#ifdef AVH_CLIENT
|
|
const AvHMapExtents& theMapExtents = gHUD.GetMapExtents();
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents();
|
|
#endif
|
|
|
|
return theMapExtents;
|
|
}
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
bool AvHSUClientTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
|
|
{
|
|
physent_t* theEntity = NULL;
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
int theFoundEntity = -1;
|
|
bool theSuccess = false;
|
|
bool theDone = false;
|
|
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
vec3_t theNormal;
|
|
VectorSubtract(inEndPos, inStartPos, theNormal);
|
|
VectorNormalize(theNormal);
|
|
|
|
outIndex = -1;
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
int theNumPlayers = gEngfuncs.GetMaxClients();
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 );
|
|
|
|
vec3_t theNewStartPos;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
|
|
VectorCopy(theNewStartPos, theStartPos);
|
|
|
|
do
|
|
{
|
|
pmtrace_t tr;
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theFoundEntity, &tr );
|
|
|
|
physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
|
|
theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );
|
|
|
|
if(pEntity)
|
|
{
|
|
// 2024 - Also skip nobuilds on client since clients can now see them.
|
|
if(pEntity->iuser3 != AVH_USER3_NONE && pEntity->iuser3 != AVH_USER3_NOBUILD)
|
|
{
|
|
VectorCopy(tr.endpos, outLocation);
|
|
outIndex = theFoundEntity;
|
|
theEntity = pEntity;
|
|
outUserThree = pEntity->iuser3;
|
|
outUserFour = pEntity->iuser4;
|
|
outTeamNumber = (AvHTeamNumber)(pEntity->team);
|
|
|
|
if(pEntity->player)
|
|
{
|
|
outPlayerWasHit = true;
|
|
}
|
|
|
|
theSuccess = true;
|
|
theDone = true;
|
|
}
|
|
}
|
|
|
|
if(tr.fraction >= (1.0f - kFloatTolerance))
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
vec3_t theDiff = theNormal*kHitOffsetAmount;
|
|
float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
|
|
if(theLength > kFloatTolerance)
|
|
{
|
|
VectorAdd(tr.endpos, theDiff, theStartPos);
|
|
}
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
// Offset a bit so we don't hit again
|
|
//VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos);
|
|
}
|
|
} while(!theDone);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
// If we didn't hit any special targets, see if it's a valid area to build or be ordered to
|
|
if(!theSuccess)
|
|
{
|
|
WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS;
|
|
theSuccess = AvHSHUClientTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode);
|
|
bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP);
|
|
|
|
if(theSuccess || theWaypointTooSteep)
|
|
{
|
|
outUserThree = AVH_USER3_WAYPOINT;
|
|
outPlayerWasHit = false;
|
|
outIndex = -1;
|
|
outTeamNumber = TEAM_IND;
|
|
theSuccess = true;
|
|
}
|
|
|
|
// Treat too steep as success, but mark it as nobuild
|
|
if(theWaypointTooSteep)
|
|
{
|
|
outUserThree = AVH_USER3_NOBUILD;
|
|
}
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
bool AvHSUServerTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
|
|
{
|
|
bool theSuccess = false;
|
|
bool theDone = false;
|
|
edict_t* theEdictToIgnore = NULL;
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
vec3_t theNormal;
|
|
VectorSubtract(inEndPos, inStartPos, theNormal);
|
|
VectorNormalize(theNormal);
|
|
|
|
// vec3_t theNewStartPos;
|
|
// AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
|
|
// VectorCopy(theNewStartPos, theStartPos);
|
|
|
|
do
|
|
{
|
|
TraceResult tr;
|
|
UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr);
|
|
CBaseEntity* theEntityHit = NULL;
|
|
|
|
// Return the entity in special way
|
|
if(tr.flFraction < 1 && tr.pHit)
|
|
{
|
|
theEntityHit = CBaseEntity::Instance(tr.pHit);
|
|
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntityHit);
|
|
AvHWeldable* theWeldable = dynamic_cast<AvHWeldable*>(theEntityHit);
|
|
|
|
AvHBasePlayerWeapon* theWeapon = dynamic_cast<AvHBasePlayerWeapon*>(theEntityHit);
|
|
|
|
if(AvHSUGetIsDebugging())
|
|
{
|
|
// Create an entity where the trace hit
|
|
Vector theAngles(0, 0, 0);
|
|
|
|
CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, tr.vecEndPos, theAngles);
|
|
ASSERT(pEnt);
|
|
pEnt->pev->movetype = MOVETYPE_FLY;
|
|
pEnt->pev->solid = SOLID_NOT;
|
|
}
|
|
|
|
if(theEntityHit && (theEntityHit->pev->iuser3 != AVH_USER3_NONE))
|
|
{
|
|
// Don't hit seethroughs
|
|
if(theEntityHit->pev->iuser3 != AVH_USER3_ALPHA)
|
|
{
|
|
outIndex = ENTINDEX(tr.pHit);
|
|
VectorCopy(tr.vecEndPos, outLocation);
|
|
outTeamNumber = (AvHTeamNumber)theEntityHit->pev->team;
|
|
outUserThree = theEntityHit->pev->iuser3;
|
|
outUserFour = theEntityHit->pev->iuser4;
|
|
|
|
if(thePlayer)
|
|
{
|
|
outPlayerWasHit = true;
|
|
}
|
|
|
|
theSuccess = true;
|
|
theDone = true;
|
|
}
|
|
}
|
|
|
|
theEdictToIgnore = theEntityHit->edict();
|
|
}
|
|
|
|
if((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance))
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
vec3_t theDiff = theNormal*kHitOffsetAmount;
|
|
float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
|
|
if(theLength > kFloatTolerance)
|
|
{
|
|
VectorAdd(theStartPos, theDiff, theStartPos);
|
|
}
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
// Offset a bit so we don't hit again
|
|
//VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos);
|
|
}
|
|
} while(!theDone);
|
|
|
|
if(!theSuccess)
|
|
{
|
|
WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS;
|
|
theSuccess = AvHSHUServerTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode);
|
|
bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP);
|
|
if(theSuccess || theWaypointTooSteep)
|
|
{
|
|
outUserThree = AVH_USER3_WAYPOINT;
|
|
outPlayerWasHit = false;
|
|
outIndex = -1;
|
|
outTeamNumber = TEAM_IND;
|
|
theSuccess = true;
|
|
}
|
|
// Treat too steep as success, but mark it as nobuild
|
|
if(theWaypointTooSteep)
|
|
{
|
|
outUserThree = AVH_USER3_NOBUILD;
|
|
}
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef AVH_CLIENT
|
|
bool AvHSHUClientTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode)
|
|
{
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
int theFoundEntity = -1;
|
|
int theEntityToIgnore = -1;
|
|
bool theDone = false;
|
|
bool theLegalToBuild = false;
|
|
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
vec3_t theNormal;
|
|
VectorSubtract(inEndPos, inStartPos, theNormal);
|
|
VectorNormalize(theNormal);
|
|
|
|
int theNumPlayers = gEngfuncs.GetMaxClients();
|
|
pmtrace_t tr;
|
|
|
|
//DebugPoint theDebugPoint(theStartPos[0], theStartPos[1], theStartPos[2]);
|
|
//gSquareDebugLocations.push_back(theDebugPoint);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1);
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
|
|
vec3_t theNewStartPos;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
|
|
VectorCopy(theNewStartPos, theStartPos);
|
|
|
|
do
|
|
{
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theEntityToIgnore, &tr );
|
|
|
|
physent_t* pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
|
|
theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );
|
|
|
|
// Trace until we hit a worldbrush or non-seethrough
|
|
if(!pEntity || (pEntity->iuser3 != AVH_USER3_ALPHA))
|
|
{
|
|
// If entity is a "no waypoint" entity we can't build here and we're done
|
|
if(pEntity && (pEntity->iuser3 == AVH_USER3_NOBUILD))
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_NOBUILD;
|
|
}
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
// else if texture is NOBUILD, we're done
|
|
const char* theTextureHitCStr = gEngfuncs.pEventAPI->EV_TraceTexture(theFoundEntity, tr.endpos, theEndPos);
|
|
if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture)))
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_NOBUILD;
|
|
}
|
|
theDone = true;
|
|
}
|
|
else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture)))
|
|
{
|
|
// Not valid, but allow it to pass through
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_SEETHROUGH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Trace texture sometimes seems to miss entities that the start point lies within. Don't count
|
|
// the trace texture if it found the texture of the next entity below it
|
|
// else if surface is more flat than vertical
|
|
if(tr.plane.normal[2] >= 0.7f)
|
|
{
|
|
// and if surface isn't under water, lava, in the sky, etc.
|
|
int thePointContents = gEngfuncs.PM_PointContents(tr.endpos, NULL);
|
|
if(thePointContents == CONTENTS_EMPTY || thePointContents == 0)
|
|
{
|
|
|
|
|
|
// and if there's enough room to build
|
|
|
|
// we can build here, and we're done
|
|
theLegalToBuild = true;
|
|
theDone = true;
|
|
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_CONTENTSFULL;
|
|
theDone = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
if(theTextureHitCStr)
|
|
{
|
|
*outReturnCode = WAYPOINT_TOOSTEEP;
|
|
}
|
|
else
|
|
{
|
|
*outReturnCode = WAYPOINT_ENTITYHIT;
|
|
}
|
|
theDone = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(theFoundEntity != 0)
|
|
{
|
|
theEntityToIgnore = theFoundEntity;
|
|
}
|
|
|
|
if(((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction == 0.0f)) /*&& !tr.startsolid && !tr.allsolid*/)
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
vec3_t theDiff = theNormal*kHitOffsetAmount;
|
|
float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
|
|
if(theLength > kFloatTolerance)
|
|
{
|
|
VectorAdd(tr.endpos, theDiff, theStartPos);
|
|
}
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
// Offset a bit so we don't hit again
|
|
//VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos);
|
|
}
|
|
} while(!theDone);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
// Always set end location to show where invalid position is
|
|
*outLocation = tr.endpos;
|
|
|
|
return theLegalToBuild;
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_CLIENT
|
|
void AvHSHUClientGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
|
|
{
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inStartPos, outNonSolidPoint);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
pmtrace_t tr;
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL, -1, &tr );
|
|
|
|
// Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine)
|
|
if((tr.startsolid) && (tr.fraction == 0.0f))
|
|
{
|
|
int theFoundEntity = -1;
|
|
bool theDone = false;
|
|
|
|
vec3_t theStartToEnd;
|
|
VectorSubtract(inEndPos, inStartPos, theStartToEnd);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
|
|
float theIncrement = 10.0f/theStartToEnd.Length();
|
|
float theT = 0.0f;
|
|
int theNumIterations = 0;
|
|
|
|
do
|
|
{
|
|
theStartPos = inStartPos + theT*theStartToEnd;
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_WORLD_ONLY, -1, &tr );
|
|
|
|
theNumIterations++;
|
|
|
|
// If start point is solid, bisect area and move start point 1/2 between current start and current end
|
|
if(tr.startsolid)
|
|
{
|
|
theT += theIncrement;
|
|
}
|
|
// else if start point isn't solid, bisect area and move start point back towards original start point
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
} while(!theDone && (theNumIterations < 200));
|
|
|
|
// Always set end location to show where invalid position is
|
|
if(!theDone)
|
|
{
|
|
outNonSolidPoint = inStartPos;
|
|
}
|
|
else
|
|
{
|
|
outNonSolidPoint = theStartPos;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
void AvHSHUServerGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
|
|
{
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inStartPos, outNonSolidPoint);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
TraceResult tr;
|
|
UTIL_TraceLine(theStartPos, theEndPos, ignore_monsters, NULL, &tr);
|
|
|
|
bool theDone = false;
|
|
|
|
// Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine)
|
|
if((tr.fStartSolid) || (tr.flFraction == 0.0f))
|
|
{
|
|
int theFoundEntity = -1;
|
|
|
|
vec3_t theStartToEnd;
|
|
VectorSubtract(inEndPos, inStartPos, theStartToEnd);
|
|
|
|
float theIncrement = 10.0f/theStartToEnd.Length();
|
|
float theT = 0.0f;
|
|
int theNumIterations = 0;
|
|
int thePointContents = 0;
|
|
|
|
do
|
|
{
|
|
theStartPos = inStartPos + theT*theStartToEnd;
|
|
thePointContents = UTIL_PointContents(theStartPos);
|
|
|
|
theNumIterations++;
|
|
|
|
// If start point is solid, bisect area and move start point 1/2 between current start and current end
|
|
if((thePointContents == CONTENTS_SOLID) || (thePointContents == CONTENTS_SKY))
|
|
{
|
|
theT += theIncrement;
|
|
}
|
|
// else if start point isn't solid, bisect area and move start point back towards original start point
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
} while(!theDone && (theNumIterations < 200));
|
|
|
|
// Always set end location to show where invalid position is
|
|
outNonSolidPoint = theStartPos;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(tr.vecEndPos, outNonSolidPoint);
|
|
theDone = true;
|
|
}
|
|
|
|
if(!theDone)
|
|
{
|
|
// When we don't hit anything, return start point
|
|
VectorCopy(inStartPos, outNonSolidPoint);
|
|
}
|
|
|
|
VectorCopy(outNonSolidPoint, gPMDebugPoint);
|
|
}
|
|
#endif
|
|
|
|
void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint)
|
|
{
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
vec3_t theNonSolidPoint;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNonSolidPoint);
|
|
|
|
VectorCopy(theNonSolidPoint, outNonSolidPoint);
|
|
}
|
|
|
|
void AvHSHUGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
|
|
{
|
|
#ifdef AVH_CLIENT
|
|
AvHSHUClientGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint);
|
|
|
|
//extern DebugPointListType gSquareDebugLocations;
|
|
//DebugPoint theDebugPoint(outNonSolidPoint[0], outNonSolidPoint[1], outNonSolidPoint[2]);
|
|
//gSquareDebugLocations.push_back(theDebugPoint);
|
|
#endif
|
|
|
|
#ifdef AVH_SERVER
|
|
AvHSHUServerGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint);
|
|
#endif
|
|
}
|
|
|
|
#ifdef AVH_SERVER
|
|
bool AvHSHUServerTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode)
|
|
{
|
|
bool theLegalToBuild = false;
|
|
bool theDone = false;
|
|
edict_t* theEdictToIgnore = NULL;
|
|
TraceResult tr;
|
|
vec3_t theStartPos;
|
|
vec3_t theEndPos;
|
|
|
|
VectorCopy(inStartPos, theStartPos);
|
|
VectorCopy(inEndPos, theEndPos);
|
|
|
|
vec3_t theNormal;
|
|
VectorSubtract(inEndPos, inStartPos, theNormal);
|
|
VectorNormalize(theNormal);
|
|
|
|
vec3_t theNewStartPos;
|
|
AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
|
|
VectorCopy(theNewStartPos, theStartPos);
|
|
|
|
do
|
|
{
|
|
UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr);
|
|
|
|
// Trace until we hit a worldbrush or non-seethrough
|
|
CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit);
|
|
CWorld* theWorld = dynamic_cast<CWorld*>(theEntityHit);
|
|
bool theHitSeethrough = (theEntityHit->pev->iuser3 == AVH_USER3_ALPHA);
|
|
if(!theHitSeethrough)
|
|
{
|
|
// If entity is a "no waypoint" entity we can't build here and we're done
|
|
if(theEntityHit && (theEntityHit->pev->iuser3 == AVH_USER3_NOBUILD))
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_NOBUILD;
|
|
}
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
edict_t* theEdict = ENT(theEntityHit->pev);
|
|
const char* theTextureHitCStr = TRACE_TEXTURE(theEdict, tr.vecEndPos, theEndPos);
|
|
if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture)))
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_NOBUILD;
|
|
}
|
|
theDone = true;
|
|
}
|
|
else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture)))
|
|
{
|
|
// Do nothing, but allow it to pass through
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_SEETHROUGH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else if surface is more flat than vertical
|
|
if(tr.vecPlaneNormal.z >= .7f)
|
|
{
|
|
// and if surface isn't under water, lava, sky
|
|
int thePointContents = UTIL_PointContents(tr.vecEndPos);
|
|
if (thePointContents == CONTENTS_EMPTY || thePointContents == 0)
|
|
{
|
|
// and if there's enough room to build
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_SUCCESS;
|
|
}
|
|
|
|
// we can build here, and we're done
|
|
theLegalToBuild = true;
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
if(outReturnCode)
|
|
{
|
|
*outReturnCode = WAYPOINT_CONTENTSFULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(theTextureHitCStr)
|
|
{
|
|
*outReturnCode = WAYPOINT_TOOSTEEP;
|
|
}
|
|
else
|
|
{
|
|
*outReturnCode = WAYPOINT_ENTITYHIT;
|
|
}
|
|
theDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!theWorld)
|
|
{
|
|
theEdictToIgnore = theEntityHit->edict();
|
|
}
|
|
|
|
if(((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) /*&& !tr.fStartSolid && !tr.fAllSolid*/)
|
|
{
|
|
theDone = true;
|
|
}
|
|
else
|
|
{
|
|
vec3_t theDiff = theNormal*kHitOffsetAmount;
|
|
float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
|
|
if(theLength > kFloatTolerance)
|
|
{
|
|
//VectorAdd(tr.vecEndPos, theDiff, theStartPos);
|
|
VectorAdd(theStartPos, theDiff, theStartPos);
|
|
}
|
|
else
|
|
{
|
|
theDone = true;
|
|
}
|
|
// Offset a bit so we don't hit again
|
|
//VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
theEdictToIgnore = theEntityHit->edict();
|
|
if(tr.vecEndPos == theStartPos)
|
|
{
|
|
vec3_t theDiff = theNormal*kHitOffsetAmount;
|
|
float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
|
|
if(theLength > kFloatTolerance)
|
|
{
|
|
//VectorAdd(tr.vecEndPos, theDiff, theStartPos);
|
|
VectorAdd(theStartPos, theDiff, theStartPos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(tr.vecEndPos, theStartPos);
|
|
}
|
|
// vec3_t theEntityPosition = AvHSHUGetRealLocation(theEntityHit->pev->origin, theEntityHit->pev->mins, theEntityHit->pev->maxs);
|
|
// //VectorCopy(theEntityPosition, theStartPos);
|
|
// theStartPos[2] = theEntityPosition[2];
|
|
}
|
|
} while(!theDone);
|
|
|
|
// Always set end location to show where invalid position is
|
|
*outLocation = tr.vecEndPos;
|
|
|
|
return theLegalToBuild;
|
|
}
|
|
#endif
|
|
|
|
bool AvHSHUTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
outPlayerWasHit = false;
|
|
outUserThree = 0;
|
|
outUserFour = 0;
|
|
|
|
// On client, set up players for prediction and do a PM_TraceLine
|
|
#ifdef AVH_CLIENT
|
|
theSuccess = AvHSUClientTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour);
|
|
#endif
|
|
|
|
// On the server, do a UTIL_TraceLine
|
|
#ifdef AVH_SERVER
|
|
theSuccess = AvHSUServerTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour);
|
|
#endif
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUTraceTangible(const Vector& inPointOfView, const Vector& inNormRay, int* outUserThree, vec3_t* outLocation, AvHTeamNumber* outTeamNumber, bool* outPlayerWasHit)
|
|
{
|
|
bool theSuccess = false;
|
|
vec3_t theTraceStart;
|
|
vec3_t theTraceEnd;
|
|
vec3_t theFoundLocation;
|
|
int theFoundIndex = -1;
|
|
int theUserThree = 0;
|
|
int theUserFour = 0;
|
|
AvHTeamNumber theTeamOfThingHit;
|
|
bool thePlayerHit = false;
|
|
|
|
// Offset a little so we don't hit the commander
|
|
VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theTraceStart);
|
|
VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theTraceEnd);
|
|
|
|
if(!outUserThree)
|
|
outUserThree = &theUserThree;
|
|
|
|
if(!outLocation)
|
|
outLocation = &theFoundLocation;
|
|
|
|
if(!outTeamNumber)
|
|
outTeamNumber = &theTeamOfThingHit;
|
|
|
|
if(!outPlayerWasHit)
|
|
outPlayerWasHit = &thePlayerHit;
|
|
|
|
theSuccess = AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, *outLocation, *outTeamNumber, *outPlayerWasHit, *outUserThree, theUserFour);
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
bool AvHSHUTraceVerticalTangible(float inX, float inY, float inZ, int& outUserThree, float& outHeight)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Create a start pos above
|
|
vec3_t theStartPos(inX, inY, inZ - 100.0f);
|
|
//vec3_t theEndPos(inX, inY, inZ - 800.0f/*-4096.0f*/);
|
|
vec3_t theEndPos(inX, inY, (float)kWorldMinimumZ);
|
|
|
|
// #ifdef AVH_CLIENT
|
|
// extern DebugPointListType gTriDebugLocations;
|
|
// bool theDrawDebug = true;//(rand() % 100 == 0);
|
|
//
|
|
// if(theDrawDebug)
|
|
// {
|
|
// DebugPoint thePoint;
|
|
// thePoint.x = theStartPos.x;
|
|
// thePoint.y = theStartPos.y;
|
|
// thePoint.z = theStartPos.z;
|
|
// gTriDebugLocations.push_back(thePoint);
|
|
//
|
|
// thePoint.z = theEndPos.z;
|
|
// gTriDebugLocations.push_back(thePoint);
|
|
// }
|
|
//
|
|
// #endif
|
|
|
|
int theIndex;
|
|
vec3_t theEndLocation;
|
|
AvHTeamNumber theTeam;
|
|
bool thePlayerHit;
|
|
int theUserFour;
|
|
|
|
theSuccess = AvHSHUTraceTangible(theStartPos, theEndPos, theIndex, theEndLocation, theTeam, thePlayerHit, outUserThree, theUserFour);
|
|
if(theSuccess)
|
|
{
|
|
outHeight = theEndLocation.z;
|
|
ASSERT(outHeight <= inZ);
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
const char* AvHSHUGetCommonSoundName(bool inIsAlien, WeaponHUDSound inHUDSound)
|
|
{
|
|
const char* theSoundName = NULL;
|
|
|
|
switch(inHUDSound)
|
|
{
|
|
case WEAPON_SOUND_HUD_ON:
|
|
theSoundName = inIsAlien ? "common/wpn_hudon-a.wav" : "common/wpn_hudon.wav";
|
|
break;
|
|
case WEAPON_SOUND_HUD_OFF:
|
|
theSoundName = inIsAlien ? "common/wpn_hudoff-a.wav" : "common/wpn_hudoff.wav";
|
|
break;
|
|
case WEAPON_SOUND_MOVE_SELECT:
|
|
theSoundName = inIsAlien ? "common/wpn_moveselect-a.wav" : "common/wpn_moveselect.wav";
|
|
break;
|
|
case WEAPON_SOUND_SELECT:
|
|
theSoundName = inIsAlien ? "common/wpn_select-a.wav" : "common/wpn_select.wav";
|
|
break;
|
|
case WEAPON_SOUND_DENYSELECT:
|
|
theSoundName = inIsAlien ? "common/wpn_denyselect-a.wav" : "common/wpn_denyselect.wav";
|
|
break;
|
|
}
|
|
|
|
return theSoundName;
|
|
}
|
|
|
|
|
|
// Values for fuser1: 0-1 means it's building. 2-3 means it's researching (see kResearchFuser1Base), 3-4 is "energy" (returns both building and researching)
|
|
const float kResearchFuser1Base = 2.0f;
|
|
const float kEnergyFuser1Base = 3.0f;
|
|
|
|
void AvHSHUGetBuildResearchState(int inUser3, int inUser4, float inFuser1, bool& outIsBuilding, bool& outIsResearching, float& outNormalizedPercentage)
|
|
{
|
|
outIsBuilding = outIsResearching = false;
|
|
|
|
float theStatus = (inFuser1/kNormalizationNetworkFactor);
|
|
|
|
if((GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)) && !GetHasUpgrade(inUser4, MASK_RECYCLING))
|
|
{
|
|
if((theStatus >= 0.0f) && (theStatus <= 1.0f))
|
|
{
|
|
outNormalizedPercentage = theStatus;
|
|
outIsBuilding = true;
|
|
}
|
|
if(inUser3 == AVH_USER3_WELD)
|
|
{
|
|
if(outNormalizedPercentage == -1)
|
|
{
|
|
outIsBuilding = outIsResearching = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((theStatus > kResearchFuser1Base) && (theStatus <= (1.0f + kResearchFuser1Base)))
|
|
{
|
|
outNormalizedPercentage = theStatus - kResearchFuser1Base;
|
|
outIsResearching = true;
|
|
}
|
|
else if((theStatus > kEnergyFuser1Base) && (theStatus <= (1.0f + kEnergyFuser1Base)))
|
|
{
|
|
// Set "energy"
|
|
outNormalizedPercentage = theStatus - kEnergyFuser1Base;
|
|
outIsBuilding = outIsResearching = true;
|
|
}
|
|
else
|
|
{
|
|
outNormalizedPercentage = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AvHSHUSetEnergyState(int inUser3, float &outFuser1, float inNormalizedPercentage)
|
|
{
|
|
outFuser1 = (kEnergyFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor;
|
|
}
|
|
|
|
void AvHSHUSetBuildResearchState(int inUser3, int inUser4, float &outFuser1, bool inTrueBuildOrFalseResearch, float inNormalizedPercentage)
|
|
{
|
|
//ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) /*|| GetHasUpgrade(inUser4, MASK_SELECTABLE)*/ || (inUser3 == AVH_USER3_WELD));
|
|
|
|
if(inNormalizedPercentage != -1)
|
|
{
|
|
inNormalizedPercentage = min(inNormalizedPercentage, 1.0f);
|
|
inNormalizedPercentage = max(inNormalizedPercentage, 0.0f);
|
|
}
|
|
|
|
// Build
|
|
if(inTrueBuildOrFalseResearch)
|
|
{
|
|
//ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD));
|
|
|
|
outFuser1 = inNormalizedPercentage*kNormalizationNetworkFactor;
|
|
}
|
|
// Research
|
|
else
|
|
{
|
|
outFuser1 = (kResearchFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor;
|
|
}
|
|
}
|
|
|
|
|
|
string AvHSHUBuildExecutableScriptName(const string& inScriptName, const string& inCurrentMapName)
|
|
{
|
|
string theExecutableScriptName = string(getModDirectory()) + string("/") + kScriptsDirectory + string("/") + inCurrentMapName + string("/") + inScriptName;
|
|
|
|
return theExecutableScriptName;
|
|
}
|
|
|
|
string AvHSHUGetTimeDateString()
|
|
{
|
|
string theTimeDateString("<date unknown>");
|
|
|
|
time_t theTimeThingie = time ((time_t *) NULL);
|
|
struct tm* theTime = localtime(&theTimeThingie);
|
|
|
|
if(theTime)
|
|
{
|
|
// 04/22/03, 16:59:12
|
|
char theBuffer[512];
|
|
sprintf(theBuffer, "%.2d/%.2d/%.2d, %.2d:%.2d:%.2d", (theTime->tm_mon + 1), theTime->tm_mday, (theTime->tm_year - 100), theTime->tm_hour, theTime->tm_min, theTime->tm_sec);
|
|
theTimeDateString = theBuffer;
|
|
}
|
|
|
|
return theTimeDateString;
|
|
}
|
|
|
|
bool AvHSHUGetNameOfLocation(const AvHBaseInfoLocationListType& inLocations, vec3_t inLocation, string& outLocation)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
// Look at our current position, and see if we lie within of the map locations
|
|
for(AvHBaseInfoLocationListType::const_iterator theIter = inLocations.begin(); theIter != inLocations.end(); theIter++)
|
|
{
|
|
if(theIter->GetIsPointInRegion(inLocation))
|
|
{
|
|
outLocation = theIter->GetLocationName();
|
|
theSuccess = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
// Also check AvHSUGetAlwaysPlayAlert to make sure some alerts are always triggered
|
|
bool AvHSHUGetForceHUDSound(AvHHUDSound inHUDSound)
|
|
{
|
|
bool theForceSound = false;
|
|
|
|
switch(inHUDSound)
|
|
{
|
|
case HUD_SOUND_ALIEN_HIVE_COMPLETE:
|
|
case HUD_SOUND_ALIEN_HIVE_DYING:
|
|
case HUD_SOUND_ALIEN_HIVE_ATTACK:
|
|
case HUD_SOUND_ALIEN_ENEMY_APPROACHES:
|
|
case HUD_SOUND_MARINE_CCUNDERATTACK:
|
|
case HUD_SOUND_SQUAD1:
|
|
case HUD_SOUND_SQUAD2:
|
|
case HUD_SOUND_SQUAD3:
|
|
case HUD_SOUND_SQUAD4:
|
|
case HUD_SOUND_SQUAD5:
|
|
theForceSound = true;
|
|
break;
|
|
}
|
|
|
|
return theForceSound;
|
|
}
|