etqw-sdk/source/game/vehicles/RouteConstraint.cpp
2008-05-29 00:00:00 +00:00

790 lines
21 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "RouteConstraint.h"
#include "../structures/DeployMask.h"
#include "../Player.h"
idCVar g_drawRouteConstraints( "g_drawRouteConstraints", "0", CVAR_INTEGER | CVAR_GAME, "draws lines showing route constraints" );
idCVar g_noRouteConstraintKick( "g_noRouteConstraintKick", "0", CVAR_BOOL | CVAR_GAME | CVAR_NOCHEAT | CVAR_RANKLOCKED, "enables/disables players being kicked for deviating from routes" );
idCVar g_noRouteMaskDestruction( "g_noRouteMaskDestruction", "0", CVAR_BOOL | CVAR_GAME | CVAR_NOCHEAT | CVAR_RANKLOCKED, "enables/disables the mcp being destroyed when driven outside the mask" );
/*
===============================================================================
sdRouteConstraintController
===============================================================================
*/
const idEventDefInternal EV_Link( "internal_link" );
CLASS_DECLARATION( idEntity, sdRouteConstraintController )
EVENT( EV_Link, sdRouteConstraintController::Event_Link )
END_CLASS
/*
================
sdRouteConstraintController::sdRouteConstraintController
================
*/
sdRouteConstraintController::sdRouteConstraintController() {
linked = false;
startPoint = NULL;
endPoint = NULL;
warningPointDeviance = 0;
maxPointDeviance = 0;
}
/*
================
sdRouteConstraintController::~sdRouteConstraintController
================
*/
sdRouteConstraintController::~sdRouteConstraintController() {
for ( int i = 0; i < points.Num(); i++ ) {
sdRoutePoint::Free( points[ i ] );
}
points.Clear();
}
/*
================
sdRouteConstraintController::Spawn
================
*/
void sdRouteConstraintController::Spawn( void ) {
PostEventMS( &EV_Link, 0 ); // Gordon: we want to handle this after OnPostMapSpawn
const char* maskName;
if ( !spawnArgs.GetString( "mask", "", &maskName ) ) {
gameLocal.Error( "sdRouteConstraintController::Spawn No Mask Specified" );
}
maskHandle = gameLocal.GetDeploymentMask( maskName );
if ( maskHandle == -1 ) {
gameLocal.Error( "sdRouteConstraintController::Spawn Invalid Mask '%s' Specified", maskName );
}
warningPointDeviance = spawnArgs.GetInt( "num_points_warning", "3" );
maxPointDeviance = spawnArgs.GetInt( "num_points_max", "6" );
const char* directionalModelName;
if ( !spawnArgs.GetString( "model_directional", "", &directionalModelName ) ) {
gameLocal.Error( "sdRouteConstraintController::Spawn No Direction Arrow Specified" );
}
directionalModel = renderModelManager->FindModel( directionalModelName );
directionalModelSkin = gameLocal.declSkinType[ spawnArgs.GetString( "skin_direction_model" ) ];
directionalModelColor = spawnArgs.GetVector( "directional_model_color" );
}
/*
================
sdRouteConstraintController::Display
================
*/
void sdRouteConstraintController::Display( sdRouteConstraintTracker* tracker ) {
idEntity* entity = tracker->GetEntity();
const idVec3& currentPosition = entity->GetPhysics()->GetOrigin();
SetRenderPoint( tracker, FindPoint( currentPosition ) );
}
/*
================
sdRouteConstraintController::SetRenderPoint
================
*/
void sdRouteConstraintController::SetRenderPoint( sdRouteConstraintTracker* tracker, sdRoutePoint* newPoint ) {
if ( tracker->GetRenderPoint() == newPoint ) {
return;
}
tracker->SetRenderPoint( newPoint );
sdRoutePoint::renderList_t& renderList = tracker->GetRenderList();
for ( int i = 0; i < renderList.Num(); i++ ) {
renderList[ i ].Hide();
}
int count = newPoint ? newPoint->GetChildren().Num() : 0;
renderList.SetNum( count );
for ( int i = 0; i < count; i++ ) {
const sdRoutePoint* child = newPoint->GetChildren()[ i ];
idVec3 middle = Lerp( newPoint->GetOrigin(), child->GetOrigin(), 0.75f );
idVec3 direction = child->GetOrigin() - newPoint->GetOrigin();
direction.Normalize();
renderEntity_t& renderEnt = renderList[ i ].GetEntity();
renderEnt.origin = middle;
renderEnt.axis = direction.ToMat3();
//gameRenderWorld->DebugAxis( renderEnt.origin, renderEnt.axis );
renderEnt.hModel = directionalModel;
renderEnt.customSkin = directionalModelSkin;
renderEnt.shaderParms[ SHADERPARM_RED ] = directionalModelColor.x;
renderEnt.shaderParms[ SHADERPARM_GREEN ] = directionalModelColor.y;
renderEnt.shaderParms[ SHADERPARM_BLUE ] = directionalModelColor.z;
renderEnt.shaderParms[ SHADERPARM_ALPHA ] = 1.f;
renderList[ i ].Show();
}
}
/*
================
sdRouteConstraintController::Update
================
*/
void sdRouteConstraintController::Update( sdRouteConstraintTracker* tracker ) {
idEntity* entity = tracker->GetEntity();
int debugValue = g_drawRouteConstraints.GetInteger();
bool drawDebug = false;
if ( debugValue == 1 ) {
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
if ( localPlayer != NULL ) {
if ( localPlayer->GetProxyEntity() == entity ) {
drawDebug = true;
}
}
} else if ( debugValue > 1 ) {
drawDebug = true;
}
if ( drawDebug ) {
if ( linked ) {
for ( int i = 0; i < points.Num(); i++ ) {
const sdRoutePoint* point = points[ i ];
const sdRoutePoint::nodeList_t& children = point->GetChildren();
for ( int j = 0; j < children.Num(); j++ ) {
const sdRoutePoint* otherPoint = children[ j ];
gameRenderWorld->DebugLine( colorRed, point->GetOrigin(), otherPoint->GetOrigin() );
}
}
const sdRoutePoint* current = tracker->GetCurrentPoint();
if ( current != NULL ) {
gameRenderWorld->DebugBounds( colorRed, idBounds( idSphere( vec3_origin, 16 ) ), current->GetOrigin() );
}
const sdRoutePoint* best = tracker->GetBestPoint();
if ( best != NULL ) {
gameRenderWorld->DebugSphere( colorGreen, idSphere( best->GetOrigin(), 16 ) );
}
const sdRoutePoint* reserve = tracker->GetBestReserve();
if ( reserve != NULL ) {
gameRenderWorld->DebugSphere( colorBlue, idSphere( reserve->GetOrigin(), 32 ) );
}
}
}
const idVec3& currentPosition = entity->GetPhysics()->GetOrigin();
bool positionValid = false;
if ( !g_noRouteMaskDestruction.GetBool() ) {
const sdPlayZone* pz = gameLocal.GetPlayZone( currentPosition, sdPlayZone::PZF_VEHICLEROUTE );
if ( pz != NULL ) {
const sdDeployMaskInstance* mask = pz->GetMask( maskHandle );
if ( mask != NULL ) {
if ( mask->IsValid( idBounds( currentPosition ) ) == DR_CLEAR ) {
positionValid = true;
}
}
}
} else {
positionValid = true;
}
// if the position isn't on the mask, don't update the node
if ( positionValid ) {
sdRoutePoint* newPoint = FindPoint( currentPosition );
if ( newPoint != NULL && ( ( newPoint->GetOrigin() - currentPosition ).Length() > 8192.f ) ) {
positionValid = false;
} else if ( tracker->GetCurrentPoint() == NULL ) {
// initial
tracker->SetBestPoint( newPoint );
} else if ( tracker->GetCurrentPoint() != newPoint ) {
bool warning = false;
bool kickPlayer = false;
int kickDistance = maxPointDeviance;
const sdRoutePoint* bestPoint = tracker->GetBestPoint();
if ( newPoint != bestPoint ) {
int distance = newPoint->OnChain( bestPoint );
if ( distance != -1 ) {
if ( !g_noRouteConstraintKick.GetBool() ) {
// we've moved backwards
kickDistance = maxPointDeviance - distance;
if ( distance >= maxPointDeviance ) {
kickPlayer = true;
} else if ( distance >= warningPointDeviance ) {
warning = true;
}
}
} else {
distance = bestPoint->OnChain( newPoint );
if ( distance != -1 ) {
// we've moved forwards, yay
tracker->SetBestPoint( newPoint );
} else {
// we're on another branch.. or something
// at worst case, this could be the start, but should never be null
const sdRoutePoint* common = bestPoint->FindCommonParent( newPoint );
if ( common == NULL ) {
gameLocal.Warning( "sdRouteConstraintController::Update Failed to find common parent" );
} else {
distance = common->OnChain( bestPoint );
if ( distance >= warningPointDeviance ) {
// we've gone way off course... or are approaching a new common point
common = bestPoint->FindCommonChild( newPoint );
if ( common == NULL ) {
gameLocal.Warning( "sdRouteConstraintController::Update Failed to find common child" );
} else {
distance = bestPoint->OnChain( common );
if ( !g_noRouteConstraintKick.GetBool() ) {
kickDistance = maxPointDeviance - distance;
if ( distance >= maxPointDeviance ) {
kickPlayer = true;
} else if ( distance >= warningPointDeviance ) {
warning = true;
}
// TODO
}
}
} else {
// short distance, so just hop over
tracker->SetBestPoint( newPoint );
}
}
}
}
}
if ( kickDistance < 0 ) {
kickDistance = 0;
}
tracker->SetKickPlayer( kickPlayer );
tracker->SetKickDistance( kickDistance );
tracker->SetPositionWarning( warning );
}
if ( positionValid ) {
tracker->SetCurrentPoint( newPoint );
}
}
tracker->SetMaskWarning( !positionValid );
}
/*
================
sdRouteConstraintController::FindPoint
================
*/
sdRoutePoint* sdRouteConstraintController::FindPoint( const idVec3& origin ) {
sdRoutePoint* bestPoint = points[ 0 ];
float bestDist = ( points[ 0 ]->GetOrigin() - origin ).LengthSqr();
for ( int i = 1; i < points.Num(); i++ ) {
float dist = ( points[ i ]->GetOrigin() - origin ).LengthSqr();
if ( dist >= bestDist ) {
continue;
}
bestDist = dist;
bestPoint = points[ i ];
}
return bestPoint;
}
/*
================
sdRouteConstraintController::FindPoint
================
*/
sdRoutePoint* sdRouteConstraintController::FindPoint( const char* name ) {
if ( linked ) {
gameLocal.Error( "sdRouteConstraintController::FindPoint Called after Linked" );
}
for ( int i = 0; i < points.Num(); i++ ) {
if ( idStr::Icmp( points[ i ]->GetName(), name ) == 0 ) {
return points[ i ];
}
}
return NULL;
}
/*
================
sdRouteConstraintController::CheckForLoops
================
*/
void sdRouteConstraintController::CheckForLoops( idList< const sdRoutePoint* >& checkPoints, const sdRoutePoint* point ) {
if ( checkPoints.FindIndex( point ) != -1 ) {
gameLocal.Error( "sdRouteConstraintController::CheckForLoops Loop Found in Path" );
}
checkPoints.Alloc() = point;
int currentSize = checkPoints.Num();
for ( int i = 0; i < point->GetChildren().Num(); i++ ) {
CheckForLoops( checkPoints, point->GetChildren()[ i ] );
checkPoints.SetNum( currentSize, false );
}
}
/*
================
sdRouteConstraintController::Event_Link
================
*/
void sdRouteConstraintController::Event_Link( void ) {
for ( int i = 0; i < points.Num(); i++ ) {
const idStrList& targetNames = points[ i ]->GetTargetNames();
for ( int j = 0; j < targetNames.Num(); j++ ) {
sdRoutePoint* otherPoint = FindPoint( targetNames[ j ].c_str() );
if ( otherPoint == NULL ) {
gameLocal.Error( "sdRouteConstraintController::Event_Link Failed to find Target '%s'", targetNames[ j ].c_str() );
}
sdRoutePoint::Link( points[ i ], otherPoint );
}
}
for ( int i = 0; i < points.Num(); i++ ) {
sdRoutePoint* point = points[ i ];
if ( point->GetParents().Num() == 0 ) {
if ( startPoint != NULL ) {
gameLocal.Error( "sdRouteConstraintController::Event_Link Multiple Start Points Found" );
}
startPoint = point;
}
if ( point->GetChildren().Num() == 0 ) {
if ( endPoint != NULL ) {
gameLocal.Error( "sdRouteConstraintController::Event_Link Multiple End Points Found" );
}
endPoint = point;
}
}
if ( startPoint == NULL ) {
gameLocal.Error( "sdRouteConstraintController::Event_Link No Start Point Found" );
}
if ( endPoint == NULL ) {
gameLocal.Error( "sdRouteConstraintController::Event_Link No End Point Found" );
}
for ( int i = 0; i < points.Num(); i++ ) {
idVec3 direction = vec3_zero;
for ( int j = 0; j < points[ i ]->GetChildren().Num(); j++ ) {
const sdRoutePoint* child = points[ i ]->GetChildren()[ j ];
idVec3 temp = child->GetOrigin() - points[ i ]->GetOrigin();
temp.Normalize();
direction += temp;
}
direction.Normalize();
points[ i ]->SetAngles( direction.ToAngles() );
}
idList< const sdRoutePoint* > checkPoints;
CheckForLoops( checkPoints, startPoint );
linked = true;
}
/*
================
sdRouteConstraintController::AddPoint
================
*/
void sdRouteConstraintController::AddPoint( sdRouteConstraintMarker* marker ) {
if ( linked ) {
gameLocal.Error( "sdRouteConstraintController::AddPoint Controller is Already Linked" );
}
sdRoutePoint* point = sdRoutePoint::Allocate();
point->Init( marker );
points.Alloc() = point;
}
/*
===============================================================================
sdRoutePoint
===============================================================================
*/
idBlockAlloc< sdRoutePoint, 32 > sdRoutePoint::s_allocator;
/*
================
sdRoutePoint::Clear
================
*/
void sdRoutePoint::Clear( void ) {
origin.Zero();
parents.Clear();
children.Clear();
name.Empty();
targetNames.Clear();
allowAirDrop = true;
}
/*
================
sdRoutePoint::Init
================
*/
void sdRoutePoint::Init( sdRouteConstraintMarker* _marker ) {
origin = _marker->GetPhysics()->GetOrigin();
allowAirDrop = !_marker->spawnArgs.GetBool( "no_air_drop" );
name = _marker->GetName();
const idKeyValue* kv = NULL;
while ( ( kv = _marker->spawnArgs.MatchPrefix( "target", kv ) ) != NULL ) {
targetNames.Append( kv->GetValue() );
}
}
/*
================
sdRoutePoint::Link
================
*/
void sdRoutePoint::Link( sdRoutePoint* parent, sdRoutePoint* child ) {
parent->AddChild( child );
child->AddParent( parent );
}
/*
================
sdRoutePoint::AddParent
================
*/
void sdRoutePoint::AddParent( sdRoutePoint* point ) {
const sdRoutePoint** parent = parents.Alloc();
if ( parent == NULL ) {
gameLocal.Error( "sdRoutePoint::AddParent '%s' Has too many parent nodes", name.c_str() );
}
*parent = point;
}
/*
================
sdRoutePoint::AddChild
================
*/
void sdRoutePoint::AddChild( sdRoutePoint* point ) {
const sdRoutePoint** child = children.Alloc();
if ( child == NULL ) {
gameLocal.Error( "sdRoutePoint::AddChild '%s' Has too many child nodes", name.c_str() );
}
*child = point;
}
/*
================
sdRoutePoint::OnChain
================
*/
int sdRoutePoint::OnChain( const sdRoutePoint* chainEnd ) const {
int distance = -1;
for ( int i = 0; i < chainEnd->parents.Num(); i++ ) {
const sdRoutePoint* parent = chainEnd->parents[ i ];
if ( parent == this ) {
return 1;
}
int temp = OnChain( parent );
if ( temp != -1 ) {
temp++;
if ( distance == -1 || temp < distance ) {
distance = temp;
}
}
}
return distance;
}
/*
================
sdRoutePoint::FindCommonParent
================
*/
const sdRoutePoint* sdRoutePoint::FindCommonParent( const sdRoutePoint* other ) const {
for ( int i = 0; i < parents.Num(); i++ ) {
const sdRoutePoint* out = FindCommonParent( other, parents[ i ] );
if ( out != NULL ) {
return out;
}
}
return NULL;
}
/*
================
sdRoutePoint::FindCommonChild
================
*/
const sdRoutePoint* sdRoutePoint::FindCommonChild( const sdRoutePoint* other ) const {
for ( int i = 0; i < children.Num(); i++ ) {
const sdRoutePoint* out = FindCommonChild( other, children[ i ] );
if ( out != NULL ) {
return out;
}
}
return NULL;
}
/*
================
sdRoutePoint::FindCommonParent
================
*/
const sdRoutePoint* sdRoutePoint::FindCommonParent( const sdRoutePoint* other, const sdRoutePoint* node ) const {
if ( node->children.Num() > 1 ) {
int distance = node->OnChain( other );
if ( distance != -1 ) {
return node;
}
}
for ( int i = 0; i < node->parents.Num(); i++ ) {
const sdRoutePoint* out = FindCommonParent( other, node->parents[ i ] );
if ( out != NULL ) {
return out;
}
}
return NULL;
}
/*
================
sdRoutePoint::FindCommonChild
================
*/
const sdRoutePoint* sdRoutePoint::FindCommonChild( const sdRoutePoint* other, const sdRoutePoint* node ) const {
if ( node->parents.Num() > 1 ) {
int distance = other->OnChain( node );
if ( distance != -1 ) {
return node;
}
}
for ( int i = 0; i < node->children.Num(); i++ ) {
const sdRoutePoint* out = FindCommonChild( other, node->children[ i ] );
if ( out != NULL ) {
return out;
}
}
return NULL;
}
/*
===============================================================================
sdRouteConstraintMarker
===============================================================================
*/
CLASS_DECLARATION( idEntity, sdRouteConstraintMarker )
END_CLASS
/*
================
sdRouteConstraintMarker::PostMapSpawn
================
*/
void sdRouteConstraintMarker::PostMapSpawn( void ) {
const char* parentName;
if ( !spawnArgs.GetString( "parent", "", &parentName ) ) {
gameLocal.Error( "sdRouteConstraintMarker::PostMapSpawn No 'parent' key specified" );
}
idEntity* parent = gameLocal.FindEntity( parentName );
if ( parent == NULL ) {
gameLocal.Error( "sdRouteConstraintMarker::PostMapSpawn Parent '%s' not found", parentName );
}
sdRouteConstraintController* controller = parent->Cast< sdRouteConstraintController >();
if ( controller == NULL ) {
gameLocal.Error( "sdRouteConstraintMarker::PostMapSpawn Parent is not of type 'sdRouteConstraintController'" );
}
controller->AddPoint( this );
PostEventMS( &EV_Remove, 0 );
}
/*
===============================================================================
sdRouteConstraintTracker
===============================================================================
*/
/*
================
sdRouteConstraintTracker::sdRouteConstraintTracker
================
*/
sdRouteConstraintTracker::sdRouteConstraintTracker( void ) {
currentPoint = NULL;
bestPoint = NULL;
bestPointReserve = NULL;
renderPoint = NULL;
positionWarning = false;
maskWarning = false;
kickPlayer = false;
kickDistance = -1;
}
/*
================
sdRouteConstraintTracker::Init
================
*/
void sdRouteConstraintTracker::Init( idEntity* _entity ) {
entity = _entity;
}
/*
================
sdRouteConstraintTracker::Update
================
*/
void sdRouteConstraintTracker::Update( void ) {
if ( !controller.IsValid() ) {
return;
}
controller->Update( this );
}
/*
================
sdRouteConstraintTracker::Display
================
*/
void sdRouteConstraintTracker::Display( void ) {
if ( !controller.IsValid() ) {
return;
}
controller->Display( this );
}
/*
================
sdRouteConstraintTracker::SetReserve
================
*/
void sdRouteConstraintTracker::SetReserve( const sdRoutePoint* point ) {
if ( point != NULL && point->GetAllowAirDrop() ) {
bestPointReserve = point;
}
}
/*
================
sdRouteConstraintTracker::GetDropLocation
================
*/
void sdRouteConstraintTracker::GetDropLocation( idVec3& position, idAngles& angles ) const {
if ( bestPointReserve != NULL ) {
position = bestPointReserve->GetOrigin();
angles = bestPointReserve->GetAngles();
return;
}
if ( controller.IsValid() ) {
const sdRoutePoint* point = controller->GetStartPoint();
if ( point != NULL ) {
position = point->GetOrigin();
angles = point->GetAngles();
return;
}
}
gameLocal.Error( "sdRouteConstraintTracker::GetDropLocation No valid location!!??!??" );
}
/*
================
sdRouteConstraintTracker::SetTrackerEntity
================
*/
void sdRouteConstraintTracker::SetTrackerEntity( idEntity* entity ) {
currentPoint = NULL;
bestPoint = NULL;
bestPointReserve = NULL;
renderPoint = NULL;
for ( int i = 0; i < markers.Num(); i++ ) {
markers[ i ].Hide();
}
markers.SetNum( 0 );
positionWarning = false;
maskWarning = false;
kickPlayer = false;
kickDistance = -1;
if ( entity == NULL ) {
controller = NULL;
return;
}
controller = entity->Cast< sdRouteConstraintController >();
if ( !controller.IsValid() ) {
gameLocal.Error( "sdRouteConstraintTracker::SetTrackerEntity Invalid Entity Passed" );
}
}
/*
================
sdRouteConstraintTracker::Hide
================
*/
void sdRouteConstraintTracker::Hide( void ) {
if ( !controller.IsValid() ) {
return;
}
controller->SetRenderPoint( this, NULL );
}