2
0
mirror of https://github.com/FWGS/hlsdk-xash3d synced 2024-11-22 18:05:23 +01:00
hlsdk-xash3d/dlls/pathcorner.cpp

412 lines
9.6 KiB
C++
Raw Normal View History

2016-06-04 15:24:23 +02:00
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
//
// ========================== PATH_CORNER ===========================
//
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "trains.h"
#include "saverestore.h"
class CPathCorner : public CPointEntity
{
public:
2016-07-31 15:48:50 +02:00
void Spawn();
2016-06-04 15:24:23 +02:00
void KeyValue( KeyValueData* pkvd );
float GetDelay( void ) { return m_flWait; }
2016-07-31 15:48:50 +02:00
//void Touch( CBaseEntity *pOther );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
2016-06-04 15:24:23 +02:00
private:
2016-07-31 15:48:50 +02:00
float m_flWait;
2016-06-04 15:24:23 +02:00
};
LINK_ENTITY_TO_CLASS( path_corner, CPathCorner )
2016-06-04 15:24:23 +02:00
// Global Savedata for Delay
TYPEDESCRIPTION CPathCorner::m_SaveData[] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD( CPathCorner, m_flWait, FIELD_FLOAT ),
};
IMPLEMENT_SAVERESTORE( CPathCorner, CPointEntity )
2016-06-04 15:24:23 +02:00
//
// Cache user-entity-field values until spawn is called.
//
2016-07-31 15:48:50 +02:00
void CPathCorner::KeyValue( KeyValueData *pkvd )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( FStrEq( pkvd->szKeyName, "wait" ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_flWait = atof( pkvd->szValue );
2016-06-04 15:24:23 +02:00
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
2016-07-31 15:48:50 +02:00
void CPathCorner::Spawn()
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ASSERTSZ( !FStringNull( pev->targetname ), "path_corner without a targetname" );
2016-06-04 15:24:23 +02:00
}
#if 0
2016-07-31 15:48:50 +02:00
void CPathCorner::Touch( CBaseEntity *pOther )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
entvars_t *pevToucher = pOther->pev;
2016-06-04 15:24:23 +02:00
2016-07-31 15:48:50 +02:00
if( FBitSet( pevToucher->flags, FL_MONSTER ) )
{
// monsters don't navigate path corners based on touch anymore
2016-06-04 15:24:23 +02:00
return;
}
// If OTHER isn't explicitly looking for this path_corner, bail out
2016-07-31 15:48:50 +02:00
if( pOther->m_pGoalEnt != this )
2016-06-04 15:24:23 +02:00
{
return;
}
// If OTHER has an enemy, this touch is incidental, ignore
2016-07-31 15:48:50 +02:00
if( !FNullEnt( pevToucher->enemy ) )
2016-06-04 15:24:23 +02:00
{
return; // fighting, not following a path
}
// UNDONE: support non-zero flWait
/*
2016-07-31 15:48:50 +02:00
if( m_flWait != 0 )
ALERT( at_warning, "Non-zero path-cornder waits NYI" );
2016-06-04 15:24:23 +02:00
*/
// Find the next "stop" on the path, make it the goal of the "toucher".
2016-07-31 15:48:50 +02:00
if( FStringNull( pev->target ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ALERT( at_warning, "PathCornerTouch: no next stop specified" );
2016-06-04 15:24:23 +02:00
}
2016-07-31 15:48:50 +02:00
pOther->m_pGoalEnt = CBaseEntity::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ) ) );
2016-06-04 15:24:23 +02:00
// If "next spot" was not found (does not exist - level design error)
2016-07-31 15:48:50 +02:00
if( !pOther->m_pGoalEnt )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ALERT( at_console, "PathCornerTouch--%s couldn't find next stop in path: %s", STRING( pev->classname ), STRING( pev->target ) );
2016-06-04 15:24:23 +02:00
return;
}
// Turn towards the next stop in the path.
2016-07-31 15:48:50 +02:00
pevToucher->ideal_yaw = UTIL_VecToYaw( pOther->m_pGoalEnt->pev->origin - pevToucher->origin );
2016-06-04 15:24:23 +02:00
}
#endif
TYPEDESCRIPTION CPathTrack::m_SaveData[] =
2016-06-04 15:24:23 +02:00
{
DEFINE_FIELD( CPathTrack, m_length, FIELD_FLOAT ),
DEFINE_FIELD( CPathTrack, m_pnext, FIELD_CLASSPTR ),
DEFINE_FIELD( CPathTrack, m_paltpath, FIELD_CLASSPTR ),
DEFINE_FIELD( CPathTrack, m_pprevious, FIELD_CLASSPTR ),
DEFINE_FIELD( CPathTrack, m_altName, FIELD_STRING ),
};
IMPLEMENT_SAVERESTORE( CPathTrack, CBaseEntity )
LINK_ENTITY_TO_CLASS( path_track, CPathTrack )
2016-06-04 15:24:23 +02:00
//
// Cache user-entity-field values until spawn is called.
//
2016-07-31 15:48:50 +02:00
void CPathTrack::KeyValue( KeyValueData *pkvd )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( FStrEq( pkvd->szKeyName, "altpath" ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
m_altName = ALLOC_STRING( pkvd->szValue );
2016-06-04 15:24:23 +02:00
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
2016-07-31 15:48:50 +02:00
void CPathTrack::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
2016-06-04 15:24:23 +02:00
{
int on;
// Use toggles between two paths
2016-07-31 15:48:50 +02:00
if( m_paltpath )
2016-06-04 15:24:23 +02:00
{
on = !FBitSet( pev->spawnflags, SF_PATH_ALTERNATE );
2016-07-31 15:48:50 +02:00
if( ShouldToggle( useType, on ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( on )
2016-06-04 15:24:23 +02:00
SetBits( pev->spawnflags, SF_PATH_ALTERNATE );
else
ClearBits( pev->spawnflags, SF_PATH_ALTERNATE );
}
}
else // Use toggles between enabled/disabled
{
on = !FBitSet( pev->spawnflags, SF_PATH_DISABLED );
2016-07-31 15:48:50 +02:00
if( ShouldToggle( useType, on ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( on )
2016-06-04 15:24:23 +02:00
SetBits( pev->spawnflags, SF_PATH_DISABLED );
else
ClearBits( pev->spawnflags, SF_PATH_DISABLED );
}
}
}
2016-07-31 15:48:50 +02:00
void CPathTrack::Link( void )
2016-06-04 15:24:23 +02:00
{
edict_t *pentTarget;
2016-07-31 15:48:50 +02:00
if( !FStringNull( pev->target ) )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ) );
if( !FNullEnt(pentTarget) )
2016-06-04 15:24:23 +02:00
{
m_pnext = CPathTrack::Instance( pentTarget );
2016-07-31 15:48:50 +02:00
if( m_pnext ) // If no next pointer, this is the end of a path
2016-06-04 15:24:23 +02:00
{
m_pnext->SetPrevious( this );
}
}
else
2016-07-31 15:48:50 +02:00
ALERT( at_console, "Dead end link %s\n", STRING( pev->target ) );
2016-06-04 15:24:23 +02:00
}
// Find "alternate" path
2016-07-31 15:48:50 +02:00
if( m_altName )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( m_altName ) );
if( !FNullEnt( pentTarget ) )
2016-06-04 15:24:23 +02:00
{
m_paltpath = CPathTrack::Instance( pentTarget );
2016-07-31 15:48:50 +02:00
if( m_paltpath ) // If no next pointer, this is the end of a path
2016-06-04 15:24:23 +02:00
{
m_paltpath->SetPrevious( this );
}
}
}
}
2016-07-31 15:48:50 +02:00
void CPathTrack::Spawn( void )
2016-06-04 15:24:23 +02:00
{
pev->solid = SOLID_TRIGGER;
2016-07-31 15:48:50 +02:00
UTIL_SetSize( pev, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
2016-06-04 15:24:23 +02:00
m_pnext = NULL;
m_pprevious = NULL;
// DEBUGGING CODE
#if PATH_SPARKLE_DEBUG
SetThink( &Sparkle );
pev->nextthink = gpGlobals->time + 0.5;
#endif
}
void CPathTrack::Activate( void )
{
2016-07-31 15:48:50 +02:00
if( !FStringNull( pev->targetname ) ) // Link to next, and back-link
2016-06-04 15:24:23 +02:00
Link();
}
2016-07-31 15:48:50 +02:00
CPathTrack *CPathTrack::ValidPath( CPathTrack *ppath, int testFlag )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !ppath )
2016-06-04 15:24:23 +02:00
return NULL;
2016-07-31 15:48:50 +02:00
if( testFlag && FBitSet( ppath->pev->spawnflags, SF_PATH_DISABLED ) )
2016-06-04 15:24:23 +02:00
return NULL;
return ppath;
}
2016-07-31 15:48:50 +02:00
void CPathTrack::Project( CPathTrack *pstart, CPathTrack *pend, Vector *origin, float dist )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( pstart && pend )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
Vector dir = pend->pev->origin - pstart->pev->origin;
2016-06-04 15:24:23 +02:00
dir = dir.Normalize();
*origin = pend->pev->origin + dir * dist;
}
}
CPathTrack *CPathTrack::GetNext( void )
{
2016-07-31 15:48:50 +02:00
if( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && !FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) )
2016-06-04 15:24:23 +02:00
return m_paltpath;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
return m_pnext;
}
CPathTrack *CPathTrack::GetPrevious( void )
{
2016-07-31 15:48:50 +02:00
if( m_paltpath && FBitSet( pev->spawnflags, SF_PATH_ALTERNATE ) && FBitSet( pev->spawnflags, SF_PATH_ALTREVERSE ) )
2016-06-04 15:24:23 +02:00
return m_paltpath;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
return m_pprevious;
}
void CPathTrack::SetPrevious( CPathTrack *pprev )
{
// Only set previous if this isn't my alternate path
2016-07-31 15:48:50 +02:00
if( pprev && !FStrEq( STRING( pprev->pev->targetname ), STRING( m_altName ) ) )
2016-06-04 15:24:23 +02:00
m_pprevious = pprev;
}
// Assumes this is ALWAYS enabled
2016-07-31 15:48:50 +02:00
CPathTrack *CPathTrack::LookAhead( Vector *origin, float dist, int move )
2016-06-04 15:24:23 +02:00
{
CPathTrack *pcurrent;
float originalDist = dist;
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
pcurrent = this;
Vector currentPos = *origin;
2016-07-31 15:48:50 +02:00
if( dist < 0 ) // Travelling backwards through path
2016-06-04 15:24:23 +02:00
{
dist = -dist;
2016-07-31 15:48:50 +02:00
while( dist > 0 )
2016-06-04 15:24:23 +02:00
{
Vector dir = pcurrent->pev->origin - currentPos;
float length = dir.Length();
2016-07-31 15:48:50 +02:00
if( !length )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !ValidPath( pcurrent->GetPrevious(), move ) ) // If there is no previous node, or it's disabled, return now.
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !move )
2016-06-04 15:24:23 +02:00
Project( pcurrent->GetNext(), pcurrent, origin, dist );
return NULL;
}
pcurrent = pcurrent->GetPrevious();
}
2016-07-31 15:48:50 +02:00
else if( length > dist ) // enough left in this path to move
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
*origin = currentPos + ( dir * ( dist / length ) );
2016-06-04 15:24:23 +02:00
return pcurrent;
}
else
{
dist -= length;
currentPos = pcurrent->pev->origin;
*origin = currentPos;
2016-07-31 15:48:50 +02:00
if( !ValidPath( pcurrent->GetPrevious(), move ) ) // If there is no previous node, or it's disabled, return now.
2016-06-04 15:24:23 +02:00
return NULL;
pcurrent = pcurrent->GetPrevious();
}
}
*origin = currentPos;
return pcurrent;
}
else
{
2016-07-31 15:48:50 +02:00
while( dist > 0 )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !ValidPath( pcurrent->GetNext(), move ) ) // If there is no next node, or it's disabled, return now.
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
if( !move )
2016-06-04 15:24:23 +02:00
Project( pcurrent->GetPrevious(), pcurrent, origin, dist );
return NULL;
}
Vector dir = pcurrent->GetNext()->pev->origin - currentPos;
float length = dir.Length();
2016-07-31 15:48:50 +02:00
if( !length && !ValidPath( pcurrent->GetNext()->GetNext(), move ) )
2016-06-04 15:24:23 +02:00
{
if ( dist == originalDist ) // HACK -- up against a dead end
return NULL;
return pcurrent;
}
2016-07-31 15:48:50 +02:00
if( length > dist ) // enough left in this path to move
2016-06-04 15:24:23 +02:00
{
*origin = currentPos + (dir * (dist / length));
return pcurrent;
}
else
{
dist -= length;
currentPos = pcurrent->GetNext()->pev->origin;
pcurrent = pcurrent->GetNext();
*origin = currentPos;
}
}
*origin = currentPos;
}
return pcurrent;
}
2016-07-31 15:48:50 +02:00
2016-06-04 15:24:23 +02:00
// Assumes this is ALWAYS enabled
2016-07-31 15:48:50 +02:00
CPathTrack *CPathTrack::Nearest( Vector origin )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
int deadCount;
float minDist, dist;
Vector delta;
CPathTrack *ppath, *pnearest;
2016-06-04 15:24:23 +02:00
delta = origin - pev->origin;
delta.z = 0;
minDist = delta.Length();
pnearest = this;
ppath = GetNext();
// Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :)
deadCount = 0;
2016-07-31 15:48:50 +02:00
while( ppath && ppath != this )
2016-06-04 15:24:23 +02:00
{
deadCount++;
2016-07-31 15:48:50 +02:00
if( deadCount > 9999 )
2016-06-04 15:24:23 +02:00
{
2016-07-31 15:48:50 +02:00
ALERT( at_error, "Bad sequence of path_tracks from %s", STRING( pev->targetname ) );
2016-06-04 15:24:23 +02:00
return NULL;
}
delta = origin - ppath->pev->origin;
delta.z = 0;
dist = delta.Length();
2016-07-31 15:48:50 +02:00
if( dist < minDist )
2016-06-04 15:24:23 +02:00
{
minDist = dist;
pnearest = ppath;
}
ppath = ppath->GetNext();
}
return pnearest;
}
CPathTrack *CPathTrack::Instance( edict_t *pent )
2016-07-31 15:48:50 +02:00
{
if( FClassnameIs( pent, "path_track" ) )
return (CPathTrack *)GET_PRIVATE( pent );
2016-06-04 15:24:23 +02:00
return NULL;
}
// DEBUGGING CODE
2016-06-04 15:24:23 +02:00
#if PATH_SPARKLE_DEBUG
2016-07-31 15:48:50 +02:00
void CPathTrack::Sparkle( void )
2016-06-04 15:24:23 +02:00
{
pev->nextthink = gpGlobals->time + 0.2;
2016-07-31 15:48:50 +02:00
if( FBitSet( pev->spawnflags, SF_PATH_DISABLED ) )
UTIL_ParticleEffect( pev->origin, Vector( 0, 0,100 ), 210, 10 );
2016-06-04 15:24:23 +02:00
else
2016-07-31 15:48:50 +02:00
UTIL_ParticleEffect( pev->origin, Vector( 0, 0, 100 ), 84, 10 );
2016-06-04 15:24:23 +02:00
}
#endif