mirror of
https://github.com/ENSL/NS.git
synced 2024-11-15 09:21:54 +00:00
3d5cb0bc6d
This is a fix for Issue #55. Changes made: * Server ignores intangible entities when determining if a building placement is valid (fixes func_breakable issue) * If a func_weldable has the "welds open" spawnflag set, then upon completing the weld when it plays the break effect, it will become fully intangible. It will reset upon round restart. Fix by @RGreenlees
3911 lines
96 KiB
C++
3911 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)/2.0f;
|
|
outLocation.y = (inMinBox.y + inMaxBox.y)/2.0f;
|
|
outLocation.z = (inMinBox.z + inMaxBox.z)/2.0f;
|
|
}
|
|
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_INTANGIBLE, 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)
|
|
{
|
|
if(pEntity->iuser3 != AVH_USER3_NONE)
|
|
{
|
|
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)
|
|
{
|
|
// 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;
|
|
}
|