//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Namespace for functions having to do with WC Edit mode
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "mathlib/mathlib.h"
# include "player.h"
# include "wcedit.h"
# include "ai_network.h"
# include "ai_initutils.h"
# include "ai_hull.h"
# include "ai_link.h"
# include "ai_node.h"
# include "ai_dynamiclink.h"
# include "ai_networkmanager.h"
# include "ndebugoverlay.h"
# include "editor_sendcommand.h"
# include "movevars_shared.h"
# include "model_types.h"
// UNDONE: Reduce some dependency here!
# include "physics_prop_ragdoll.h"
# include "items.h"
# include "utlsymbol.h"
# include "physobj.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern float GetFloorZ ( const Vector & origin ) ;
//-----------------------------------------------------------------------------
// Purpose: Make sure the version of the map in WC is the same as the map
// that's being edited
// Input :
// Output :
//-----------------------------------------------------------------------------
bool NWCEdit : : IsWCVersionValid ( void )
{
int status = Editor_CheckVersion ( STRING ( gpGlobals - > mapname ) , gpGlobals - > mapversion , false ) ;
if ( ! status )
{
return true ;
}
else if ( status = = Editor_NotRunning )
{
Msg ( " \n Aborting map_edit \n Worldcraft not running... \n \n " ) ;
UTIL_CenterPrintAll ( " Worldcraft not running... " ) ;
engine - > ServerCommand ( " disconnect \n " ) ;
}
else
{
Msg ( " \n Aborting map_edit \n WC/Engine map versions different... \n \n " ) ;
UTIL_CenterPrintAll ( " WC/Engine map versions different... " ) ;
engine - > ServerCommand ( " disconnect \n " ) ;
}
return false ;
}
//------------------------------------------------------------------------------
// Purpose : Figure out placement position of air nodes form where player is
// looking. Keep distance from player constant but adjust height
// based on viewing angle
// Input :
// Output :
//------------------------------------------------------------------------------
Vector NWCEdit : : AirNodePlacementPosition ( void )
{
AssertMsg ( false , " Air node placement in wcedit broken in Left4Dead. " ) ;
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( CBaseEntity : : m_nDebugPlayer ) ;
if ( ! pPlayer )
{
return vec3_origin ;
}
Vector pForward ;
pPlayer - > EyeVectors ( & pForward ) ;
Vector floorVec = pForward ;
floorVec . z = 0 ;
VectorNormalize ( floorVec ) ;
VectorNormalize ( pForward ) ;
float cosAngle = DotProduct ( floorVec , pForward ) ;
float lookDist = g_pAINetworkManager - > GetEditOps ( ) - > m_flAirEditDistance / cosAngle ;
Vector lookPos = pPlayer - > EyePosition ( ) + pForward * lookDist ;
return lookPos ;
}
//-----------------------------------------------------------------------------
// Purpose: For create nodes in wc edit mode
// Input :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit : : CreateAINode ( CBasePlayer * pPlayer )
{
// -------------------------------------------------------------
// Check that WC is running with the right map version
// -------------------------------------------------------------
if ( ! IsWCVersionValid ( ) | | ! pPlayer )
return ;
pPlayer - > AddSolidFlags ( FSOLID_NOT_SOLID ) ;
int hullType = g_pAINetworkManager - > GetEditOps ( ) - > m_iHullDrawNum ;
// -----------------------------------
// Get position of node to create
// -----------------------------------
Vector vNewNodePos = vec3_origin ;
bool bPositionValid = false ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
vNewNodePos = NWCEdit : : AirNodePlacementPosition ( ) ;
// Make sure we can see the node
trace_t tr ;
UTIL_TraceLine ( pPlayer - > EyePosition ( ) , vNewNodePos , MASK_NPCSOLID_BRUSHONLY , pPlayer , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction = = 1.0 )
{
bPositionValid = true ;
}
}
else
{
// Place node by where the player is looking
Vector forward ;
pPlayer - > EyeVectors ( & forward ) ;
Vector startTrace = pPlayer - > EyePosition ( ) ;
Vector endTrace = pPlayer - > EyePosition ( ) + forward * MAX_TRACE_LENGTH ;
trace_t tr ;
UTIL_TraceLine ( startTrace , endTrace , MASK_NPCSOLID , pPlayer , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction ! = 1.0 )
{
// Raise the end position up off the floor, place the node and drop him down
tr . endpos . z + = 48 ;
vNewNodePos = tr . endpos ;
bPositionValid = true ;
}
}
// -------------------------------------------------------------------------------
// Now check that this is a valid location for the new node bu using test hull
// -------------------------------------------------------------------------------
if ( bPositionValid )
{
CBaseEntity * testHull = ( CBaseEntity * ) CAI_TestHull : : GetTestHull ( ) ;
// Set the size of the test hull
UTIL_SetSize ( testHull , NAI_Hull : : Mins ( hullType ) , NAI_Hull : : Maxs ( hullType ) ) ;
// Set origin of test hull
testHull - > SetLocalOrigin ( vNewNodePos ) ;
// -----------------------------------------------------------------------
// If a ground node, drop to floor and make sure can stand at test postion
// -----------------------------------------------------------------------
if ( ! g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
UTIL_DropToFloor ( testHull , MASK_NPCSOLID ) ;
vNewNodePos = testHull - > GetAbsOrigin ( ) ;
CTraceFilterSimple traceFilter ( testHull , COLLISION_GROUP_NONE ) ;
if ( ! UTIL_CheckBottom ( testHull , & traceFilter , sv_stepsize . GetFloat ( ) ) )
{
CAI_TestHull : : ReturnTestHull ( ) ;
bPositionValid = false ;
goto DoneCreate ;
}
}
// -----------------------------------------------------------------------
// Make sure hull fits at location by seeing if it can move up a fraction
// -----------------------------------------------------------------------
Vector vUpBit = testHull - > GetAbsOrigin ( ) ;
vUpBit . z + = 1 ;
trace_t tr ;
UTIL_TraceHull ( testHull - > GetAbsOrigin ( ) , vUpBit , NAI_Hull : : Mins ( hullType ) ,
NAI_Hull : : Maxs ( hullType ) , MASK_NPCSOLID , testHull , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . startsolid | | tr . fraction ! = 1.0 )
{
CAI_TestHull : : ReturnTestHull ( ) ;
bPositionValid = false ;
goto DoneCreate ;
}
// <<TEMP>> Round position till DS fixed WC bug
testHull - > SetLocalOrigin ( Vector ( floor ( testHull - > GetAbsOrigin ( ) . x ) ,
floor ( testHull - > GetAbsOrigin ( ) . y ) , floor ( testHull - > GetAbsOrigin ( ) . z ) ) ) ;
// ---------------------------------------
// Send new node to WC
// ---------------------------------------
int status ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
status = Editor_CreateNode ( " info_node_air " , g_pAINetworkManager - > GetEditOps ( ) - > m_nNextWCIndex , testHull - > GetLocalOrigin ( ) . x , testHull - > GetLocalOrigin ( ) . y , testHull - > GetLocalOrigin ( ) . z , false ) ;
}
else
{
// Create slightly higher in WC so it can be dropped when its loaded again
Vector origin = testHull - > GetLocalOrigin ( ) ;
origin . z + = 24.0 ;
testHull - > SetLocalOrigin ( origin ) ;
status = Editor_CreateNode ( " info_node " , g_pAINetworkManager - > GetEditOps ( ) - > m_nNextWCIndex , testHull - > GetLocalOrigin ( ) . x , testHull - > GetLocalOrigin ( ) . y , testHull - > GetLocalOrigin ( ) . z , false ) ;
}
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on creation... \n " ) ;
CAI_TestHull : : ReturnTestHull ( ) ;
}
else if ( status = = Editor_OK )
{
// -----------------------
// Create a new ai node
// -----------------------
CNodeEnt * pNodeEnt ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
pNodeEnt = ( CNodeEnt * ) CreateEntityByName ( " info_node_air " ) ;
}
else
{
pNodeEnt = ( CNodeEnt * ) CreateEntityByName ( " info_node " ) ;
}
// Note this is a new entity being created as part of wc editing
pNodeEnt - > SetLocalOrigin ( testHull - > GetLocalOrigin ( ) ) ;
CAI_TestHull : : ReturnTestHull ( ) ;
pNodeEnt - > m_NodeData . nWCNodeID = g_pAINetworkManager - > GetEditOps ( ) - > m_nNextWCIndex ;
pNodeEnt - > m_debugOverlays | = OVERLAY_WC_CHANGE_ENTITY ;
pNodeEnt - > Spawn ( ) ;
}
}
DoneCreate :
// ----------------------------------------------------------
// Flash a red box as a warning that the hull won't fit here
// ----------------------------------------------------------
if ( ! bPositionValid )
{
NDebugOverlay : : Box ( vNewNodePos , NAI_Hull : : Mins ( hullType ) , NAI_Hull : : Maxs ( hullType ) , 255 , 0 , 0 , 0 , 0.1 ) ;
}
// Restore player collidability
pPlayer - > SetSolid ( SOLID_BBOX ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit : : UndoDestroyAINode ( void )
{
// -------------------------------------------------------------
// Check that WC is running with the right map version
// -------------------------------------------------------------
if ( ! IsWCVersionValid ( ) )
{
return ;
}
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode )
{
Vector nodePos = g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode - > GetOrigin ( ) ;
int status ;
int nOldWCID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode - > GetId ( ) ] ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
status = Editor_CreateNode ( " info_node_air " , nOldWCID , nodePos . x , nodePos . y , nodePos . z , false ) ;
}
else
{
status = Editor_CreateNode ( " info_node " , nOldWCID , nodePos . x , nodePos . y , nodePos . z , false ) ;
}
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on creation... \n " ) ;
}
else if ( status = = Editor_OK )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode - > SetType ( NODE_GROUND ) ;
//@ tofo g_pAINetworkManager->GetEditOps()->m_pLastDeletedNode->m_pNetwork->BuildNetworkGraph();
g_pAINetworkManager - > BuildNetworkGraph ( ) ;
g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode = NULL ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: For destroying nodes in wc edit mode
// Input :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit : : DestroyAINode ( CBasePlayer * pPlayer )
{
// -------------------------------------------------------------
// Check that WC is running with the right map version
// -------------------------------------------------------------
if ( ! IsWCVersionValid ( ) )
{
return ;
}
if ( ! pPlayer )
{
return ;
}
NodeType_e nNodeType = NODE_GROUND ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
nNodeType = NODE_AIR ;
}
CAI_Node * pAINode = pPlayer - > FindPickerAINode ( nNodeType ) ;
if ( pAINode )
{
int status = Editor_DeleteNode ( g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ pAINode - > GetId ( ) ] , false ) ;
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on deletion... \n " ) ;
}
else if ( status = = Editor_OK )
{
// Mark this node as deleted and changed
pAINode - > SetType ( NODE_DELETED ) ;
pAINode - > m_eNodeInfo | = bits_NODE_WC_CHANGED ;
// Note that network needs to be rebuild
g_pAINetworkManager - > GetEditOps ( ) - > SetRebuildFlags ( ) ;
g_pAINetworkManager - > GetEditOps ( ) - > m_pLastDeletedNode = pAINode ;
// Now go through at delete any dynamic links that were attached to this node
for ( int link = 0 ; link < pAINode - > NumLinks ( ) ; link + + )
{
int nSrcID = pAINode - > GetLinkByIndex ( link ) - > m_iSrcID ;
int nDstID = pAINode - > GetLinkByIndex ( link ) - > m_iDestID ;
if ( CAI_DynamicLink : : GetDynamicLink ( nSrcID , nDstID ) )
{
int nWCSrcID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ nSrcID ] ;
int nWCDstID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ nDstID ] ;
int status = Editor_DeleteNodeLink ( nWCSrcID , nWCDstID ) ;
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on node link deletion... \n " ) ;
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: For restroring links in WC edit mode. This actually means
// destroying links in WC that have been marked as
// Input :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit : : CreateAILink ( CBasePlayer * pPlayer )
{
// -------------------------------------------------------------
// Check that WC is running with the right map version
// -------------------------------------------------------------
if ( ! IsWCVersionValid ( ) | | ! pPlayer )
{
return ;
}
CAI_Link * pAILink = pPlayer - > FindPickerAILink ( ) ;
if ( pAILink & & ( pAILink - > m_LinkInfo & bits_LINK_OFF ) )
{
int nWCSrcID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ pAILink - > m_iSrcID ] ;
int nWCDstID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ pAILink - > m_iDestID ] ;
int status = Editor_DeleteNodeLink ( nWCSrcID , nWCDstID , false ) ;
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on node link creation... \n " ) ;
}
else if ( status = = Editor_OK )
{
// Don't actually destroy the dynamic link while editing. Just mark the link
pAILink - > m_LinkInfo & = ~ bits_LINK_OFF ;
CAI_DynamicLink * pDynamicLink = CAI_DynamicLink : : GetDynamicLink ( pAILink - > m_iSrcID , pAILink - > m_iDestID ) ;
UTIL_Remove ( pDynamicLink ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: For destroying links in wc edit mode. Actually have to create
// a link in WC that is marked as off
// Input :
// Output :
//-----------------------------------------------------------------------------
void NWCEdit : : DestroyAILink ( CBasePlayer * pPlayer )
{
// -------------------------------------------------------------
// Check that WC is running with the right map version
// -------------------------------------------------------------
if ( ! IsWCVersionValid ( ) | | ! pPlayer )
{
return ;
}
CAI_Link * pAILink = pPlayer - > FindPickerAILink ( ) ;
if ( pAILink )
{
int nWCSrcID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ pAILink - > m_iSrcID ] ;
int nWCDstID = g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ pAILink - > m_iDestID ] ;
int status = Editor_CreateNodeLink ( nWCSrcID , nWCDstID , false ) ;
if ( status = = Editor_BadCommand )
{
Msg ( " Worldcraft failed on node link creation... \n " ) ;
}
else if ( status = = Editor_OK )
{
// Create dynamic link and mark the link
CAI_DynamicLink * pNewLink = ( CAI_DynamicLink * ) CreateEntityByName ( " info_node_link " ) ; ;
pNewLink - > m_nSrcID = pAILink - > m_iSrcID ;
pNewLink - > m_nDestID = pAILink - > m_iDestID ;
pNewLink - > m_nLinkState = LINK_OFF ;
pAILink - > m_LinkInfo | = bits_LINK_OFF ;
}
}
}
Vector * g_EntityPositions = NULL ;
QAngle * g_EntityOrientations = NULL ;
string_t * g_EntityClassnames = NULL ;
//-----------------------------------------------------------------------------
// Purpose: Saves the entity's position for future communication with Hammer
//-----------------------------------------------------------------------------
void NWCEdit : : RememberEntityPosition ( CBaseEntity * pEntity )
{
if ( ! ( pEntity - > ObjectCaps ( ) & FCAP_WCEDIT_POSITION ) )
return ;
if ( ! g_EntityPositions )
{
g_EntityPositions = new Vector [ NUM_ENT_ENTRIES ] ;
g_EntityOrientations = new QAngle [ NUM_ENT_ENTRIES ] ;
// have to save these too because some entities change the classname on spawn (e.g. prop_physics_override, physics_prop)
g_EntityClassnames = new string_t [ NUM_ENT_ENTRIES ] ;
}
int index = pEntity - > entindex ( ) ;
g_EntityPositions [ index ] = pEntity - > GetAbsOrigin ( ) ;
g_EntityOrientations [ index ] = pEntity - > GetAbsAngles ( ) ;
g_EntityClassnames [ index ] = pEntity - > m_iClassname ;
}
//-----------------------------------------------------------------------------
// Purpose: Sends Hammer an update to the current position
//-----------------------------------------------------------------------------
void NWCEdit : : UpdateEntityPosition ( CBaseEntity * pEntity )
{
const Vector & newPos = pEntity - > GetAbsOrigin ( ) ;
const QAngle & newAng = pEntity - > GetAbsAngles ( ) ;
DevMsg ( 1 , " %s \n origin %f %f %f \n angles %f %f %f \n " , pEntity - > GetClassname ( ) , newPos . x , newPos . y , newPos . z , newAng . x , newAng . y , newAng . z ) ;
if ( Ragdoll_IsPropRagdoll ( pEntity ) )
{
char tmp [ 2048 ] ;
Ragdoll_GetAngleOverrideString ( tmp , sizeof ( tmp ) , pEntity ) ;
DevMsg ( 1 , " pose: %s \n " , tmp ) ;
}
if ( ! ( pEntity - > ObjectCaps ( ) & FCAP_WCEDIT_POSITION ) )
return ;
// can't do this unless in edit mode
if ( ! engine - > IsInEditMode ( ) )
return ;
int entIndex = pEntity - > entindex ( ) ;
Vector pos = g_EntityPositions [ entIndex ] ;
EditorSendResult_t result = Editor_BadCommand ;
const char * pClassname = STRING ( g_EntityClassnames [ entIndex ] ) ;
if ( pEntity - > GetModel ( ) & & modelinfo - > GetModelType ( pEntity - > GetModel ( ) ) = = mod_brush )
{
QAngle xformAngles ;
RotationDelta ( g_EntityOrientations [ entIndex ] , newAng , & xformAngles ) ;
if ( xformAngles . Length ( ) > 1e-4 )
{
result = Editor_RotateEntity ( pClassname , pos . x , pos . y , pos . z , xformAngles ) ;
}
else
{
// don't call through for an identity rotation, may just increase error
result = Editor_OK ;
}
}
else
{
if ( Ragdoll_IsPropRagdoll ( pEntity ) )
{
char tmp [ 2048 ] ;
Ragdoll_GetAngleOverrideString ( tmp , sizeof ( tmp ) , pEntity ) ;
result = Editor_SetKeyValue ( pClassname , pos . x , pos . y , pos . z , " angleOverride " , tmp ) ;
if ( result ! = Editor_OK )
goto error ;
}
result = Editor_SetKeyValue ( pClassname , pos . x , pos . y , pos . z , " angles " , CFmtStr ( " %f %f %f " , newAng . x , newAng . y , newAng . z ) ) ;
}
if ( result ! = Editor_OK )
goto error ;
result = Editor_SetKeyValue ( pClassname , pos . x , pos . y , pos . z , " origin " , CFmtStr ( " %f %f %f " , newPos . x , newPos . y , newPos . z ) ) ;
if ( result ! = Editor_OK )
goto error ;
NDebugOverlay : : EntityBounds ( pEntity , 0 , 255 , 0 , 0 , 5 ) ;
// save the update
RememberEntityPosition ( pEntity ) ;
return ;
error :
NDebugOverlay : : EntityBounds ( pEntity , 255 , 0 , 0 , 0 , 5 ) ;
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_Create ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) )
{
CBaseEntity : : m_nDebugPlayer = UTIL_GetCommandClientIndex ( ) ;
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bLinkEditMode )
{
NWCEdit : : CreateAILink ( UTIL_GetCommandClient ( ) ) ;
}
else
{
NWCEdit : : CreateAINode ( UTIL_GetCommandClient ( ) ) ;
}
}
}
static ConCommand wc_create ( " wc_create " , CC_WC_Create , " When in WC edit mode, creates a node where the player is looking if a node is allowed at that location for the currently selected hull size (see ai_next_hull) " , FCVAR_CHEAT) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_Destroy ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) )
{
CBaseEntity : : m_nDebugPlayer = UTIL_GetCommandClientIndex ( ) ;
// UNDONE: For now just deal with info_nodes
//CBaseEntity* pEntity = FindEntity( pEdict, ""); - use when generalize this to any class
//int status = Editor_DeleteEntity("info_node", pEdict->origin.x, pEdict->origin.y, pEdict->origin.z, false);
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bLinkEditMode )
{
NWCEdit : : DestroyAILink ( UTIL_GetCommandClient ( ) ) ;
}
else
{
NWCEdit : : DestroyAINode ( UTIL_GetCommandClient ( ) ) ;
}
}
}
static ConCommand wc_destroy ( " wc_destroy " , CC_WC_Destroy , " When in WC edit mode, destroys the node that the player is nearest to looking at. (The node will be highlighted by a red box) . " , FCVAR_CHEAT) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_DestroyUndo ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) )
{
CBaseEntity : : m_nDebugPlayer = UTIL_GetCommandClientIndex ( ) ;
NWCEdit : : UndoDestroyAINode ( ) ;
}
}
static ConCommand wc_destroy_undo ( " wc_destroy_undo " , CC_WC_DestroyUndo , " When in WC edit mode restores the last deleted node " , FCVAR_CHEAT ) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_AirNodeEdit ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) )
{
// Toggle air edit mode state
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode = false ;
}
else
{
g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode = true ;
}
}
}
static ConCommand wc_air_node_edit ( " wc_air_node_edit " , CC_WC_AirNodeEdit , " When in WC edit mode, toggles laying down or air nodes instead of ground nodes " , FCVAR_CHEAT ) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_AirNodeEditFurther ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) & & g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_flAirEditDistance + = 10.0 ;
}
}
static ConCommand wc_air_edit_further ( " wc_air_edit_further " , CC_WC_AirNodeEditFurther , " When in WC edit mode and editing air nodes, moves position of air node crosshair and placement location further away from player " , FCVAR_CHEAT ) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_AirNodeEditNearer ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) & & g_pAINetworkManager - > GetEditOps ( ) - > m_bAirEditMode )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_flAirEditDistance - = 10.0 ;
}
}
static ConCommand wc_air_edit_nearer ( " wc_air_edit_nearer " , CC_WC_AirNodeEditNearer , " When in WC edit mode and editing air nodes, moves position of air node crosshair and placement location nearer to from player " , FCVAR_CHEAT ) ;
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CC_WC_LinkEdit ( void )
{
// Only allowed in wc_edit_mode
if ( engine - > IsInEditMode ( ) )
{
// Toggle air edit mode state
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_bLinkEditMode )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_bLinkEditMode = false ;
}
// Don't allow link mode if graph outdated
else if ( ! ( g_pAINetworkManager - > GetEditOps ( ) - > m_debugNetOverlays & bits_debugNeedRebuild ) )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_bLinkEditMode = true ;
}
}
}
static ConCommand wc_link_edit ( " wc_link_edit " , CC_WC_LinkEdit , 0 , FCVAR_CHEAT ) ;
/// This is an entity used by the hammer_update_safe_entities command. It allows designers
/// to specify objects that should be ignored. It stores an array of sixteen strings
/// which may correspond to names. Designers may ignore more than sixteen objects by
/// placing more than one of these in a level.
class CWC_UpdateIgnoreList : public CBaseEntity
{
public :
DECLARE_CLASS ( CWC_UpdateIgnoreList , CBaseEntity ) ;
enum { MAX_IGNORELIST_NAMES = 16 } ; ///< the number of names in the array below
inline const string_t & GetName ( int x ) const { return m_nIgnoredEntityNames [ x ] ; }
protected :
// the list of names to ignore
string_t m_nIgnoredEntityNames [ MAX_IGNORELIST_NAMES ] ;
public :
DECLARE_DATADESC ( ) ;
} ;
LINK_ENTITY_TO_CLASS ( hammer_updateignorelist , CWC_UpdateIgnoreList ) ;
BEGIN_DATADESC ( CWC_UpdateIgnoreList )
// Be still, classcheck!
//DEFINE_FIELD( m_nIgnoredEntityNames, FIELD_STRING, MAX_IGNORELIST_NAMES ),
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 0 ] , FIELD_STRING , " IgnoredName01 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 1 ] , FIELD_STRING , " IgnoredName02 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 2 ] , FIELD_STRING , " IgnoredName03 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 3 ] , FIELD_STRING , " IgnoredName04 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 4 ] , FIELD_STRING , " IgnoredName05 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 5 ] , FIELD_STRING , " IgnoredName06 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 6 ] , FIELD_STRING , " IgnoredName07 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 7 ] , FIELD_STRING , " IgnoredName08 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 8 ] , FIELD_STRING , " IgnoredName09 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 9 ] , FIELD_STRING , " IgnoredName10 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 10 ] , FIELD_STRING , " IgnoredName11 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 11 ] , FIELD_STRING , " IgnoredName12 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 12 ] , FIELD_STRING , " IgnoredName13 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 13 ] , FIELD_STRING , " IgnoredName14 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 14 ] , FIELD_STRING , " IgnoredName15 " ) ,
DEFINE_KEYFIELD ( m_nIgnoredEntityNames [ 15 ] , FIELD_STRING , " IgnoredName16 " ) ,
END_DATADESC ( )
CON_COMMAND ( hammer_update_entity , " Updates the entity's position/angles when in edit mode " )
{
if ( args . ArgC ( ) < 2 )
{
CBasePlayer * pPlayer = UTIL_GetCommandClient ( ) ;
trace_t tr ;
Vector forward ;
pPlayer - > EyeVectors ( & forward ) ;
UTIL_TraceLine ( pPlayer - > EyePosition ( ) , pPlayer - > EyePosition ( ) + forward * MAX_COORD_RANGE ,
MASK_SHOT_HULL | CONTENTS_GRATE | CONTENTS_DEBRIS , pPlayer , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . DidHit ( ) & & ! tr . DidHitWorld ( ) )
{
NWCEdit : : UpdateEntityPosition ( tr . m_pEnt ) ;
}
}
else
{
CBaseEntity * pEnt = NULL ;
while ( ( pEnt = gEntList . FindEntityGeneric ( pEnt , args [ 1 ] ) ) ! = NULL )
{
NWCEdit : : UpdateEntityPosition ( pEnt ) ;
}
}
}
CON_COMMAND ( hammer_update_safe_entities , " Updates entities in the map that can safely be updated (don't have parents or are affected by constraints). Also excludes entities mentioned in any hammer_updateignorelist objects in this map. " )
{
int iCount = 0 ;
CBaseEntity * pEnt = NULL ;
Msg ( " \n ==================================================== \n Performing Safe Entity Update \n " ) ;
// first look for any exclusion objects -- these are entities that list specific things to be ignored.
// All the names that are inside them, we store into a hash table (here implemented through a local
// CUtlSymbolTable)
CUtlSymbolTable ignoredNames ( 16 , 32 , true ) ; // grow 16 strings at a time. Case insensitive.
while ( ( pEnt = gEntList . FindEntityByClassname ( pEnt , " hammer_updateignorelist " ) ) ! = NULL )
{
// for each name in each of those strings, add it to the symbol table.
CWC_UpdateIgnoreList * piglist = static_cast < CWC_UpdateIgnoreList * > ( pEnt ) ;
for ( int ii = 0 ; ii < CWC_UpdateIgnoreList : : MAX_IGNORELIST_NAMES ; + + ii )
{
if ( ! ! piglist - > GetName ( ii ) ) // if not null
{ // add to symtab
ignoredNames . AddString ( piglist - > GetName ( ii ) . ToCStr ( ) ) ;
}
}
}
if ( ignoredNames . GetNumStrings ( ) > 0 )
{
Msg ( " Ignoring %d specified targetnames. \n " , ignoredNames . GetNumStrings ( ) ) ;
}
// now iterate through everything in the world
for ( pEnt = gEntList . FirstEnt ( ) ; pEnt ! = NULL ; pEnt = gEntList . NextEnt ( pEnt ) )
{
if ( ! ( pEnt - > ObjectCaps ( ) & FCAP_WCEDIT_POSITION ) )
continue ;
// If we have a parent, or any children, we're not safe to update
if ( pEnt - > GetMoveParent ( ) | | pEnt - > FirstMoveChild ( ) )
continue ;
IPhysicsObject * pPhysics = pEnt - > VPhysicsGetObject ( ) ;
if ( ! pPhysics )
continue ;
// If we are affected by any constraints, we're not safe to update
if ( pPhysics - > IsAttachedToConstraint ( Ragdoll_IsPropRagdoll ( pEnt ) ) )
continue ;
// Motion disabled?
if ( ! pPhysics - > IsMoveable ( ) )
continue ;
// ignore brush models (per bug 61318)
if ( dynamic_cast < CPhysBox * > ( pEnt ) )
continue ;
// explicitly excluded by designer?
if ( ignoredNames . Find ( pEnt - > GetEntityName ( ) . ToCStr ( ) ) . IsValid ( ) )
continue ;
NWCEdit : : UpdateEntityPosition ( pEnt ) ;
iCount + + ;
}
Msg ( " Updated %d entities. \n " , iCount ) ;
}