ef2gamesource/dlls/game/gravpath.cpp

700 lines
14 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /EF2/Code/DLLs/game/gravpath.cpp $
// $Revision:: 8 $
// $Author:: Singlis $
// $Date:: 9/26/03 2:36p $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
// DESCRIPTION:
// Gravity path - Used for underwater currents and wells.
#include "_pch_cpp.h"
#include "entity.h"
#include "gravpath.h"
#include "container.h"
#include "navigate.h"
#include "misc.h"
#include "player.h"
GravPathManager gravPathManager;
CLASS_DECLARATION( Class, GravPathManager, NULL )
{
{ NULL,NULL }
};
GravPathManager::~GravPathManager()
{
Reset();
}
void GravPathManager::Reset( void )
{
while( pathList.NumObjects() > 0 )
{
delete ( GravPath * )pathList.ObjectAt( 1 );
}
pathList.FreeObjectList();
}
void GravPathManager::AddPath(GravPath *p)
{
pathList.AddObject( p );
}
void GravPathManager::RemovePath(GravPath *p)
{
pathList.RemoveObject( p );
}
void GravPathManager::DrawGravPaths( void )
{
int i;
int num = pathList.NumObjects();
for( i = 1; i <= num; i++ )
{
GravPath *p = ( GravPath * )pathList.ObjectAt( i );
p->DrawPath( 1.0f, 0.0f, 0.0f );
}
}
Vector GravPathManager::CalculateGravityPull(Entity &ent, const Vector &pos, qboolean *force, float *max_speed)
{
int i,num;
GravPath *p;
GravPathNode *node;
Vector point;
Vector newpoint;
Vector dir;
float bestdist = 99999;
float dist;
float speed;
float radius;
Vector velocity;
int bestpath = 0;
int entity_contents, grav_contents;
*force = false;
num = pathList.NumObjects();
if ( !num )
{
return vec_zero;
}
entity_contents = gi.pointcontents( ent.origin, 0 );
for( i = 1; i <= num; i++ )
{
p = ( GravPath * )pathList.ObjectAt( i );
if ( !p )
continue;
// Check to see if path is active
node = p->GetNode( 1 );
if ( !node || !node->active )
continue;
// Check to see if the contents are the same
grav_contents = gi.pointcontents( node->origin, 0 );
// If grav node is in water, make sure ent is too.
if ( ( grav_contents & CONTENTS_WATER ) && !( entity_contents & CONTENTS_WATER ) )
continue;
// Test to see if we are in this path's bounding box
if ( (pos.x < p->maxs.x) && (pos.y < p->maxs.y) && (pos.z < p->maxs.z) &&
(pos.x > p->mins.x) && (pos.y > p->mins.y) && (pos.z > p->mins.z) )
{
point = p->ClosestPointOnPath(pos, ent, &dist, &speed, &radius);
// If the closest distance on the path is greater than the radius, then
// do not consider this path.
if (dist > radius)
{
continue;
}
else if (dist < bestdist)
{
bestpath = i;
bestdist = dist;
}
}
}
if (!bestpath)
{
return vec_zero;
}
p = ( GravPath * )pathList.ObjectAt( bestpath );
if ( !p )
return velocity;
*force = p->force;
dist = p->DistanceAlongPath(pos, &speed);
newpoint = p->PointAtDistance( pos, dist + speed, ent.isSubclassOf( Player ), max_speed );
dir = newpoint-pos;
dir.normalize();
velocity = dir * speed;
//velocity *= .75;
return velocity;
}
/*****************************************************************************/
/*QUAKED info_grav_pathnode (0 0 .5) (-16 -16 0) (16 16 32) HEADNODE FORCE PULL_UPWARDS
"radius" Radius of the effect of the pull (Default is 256)
"speed" Speed of the pull (Use negative for a repulsion) (Default is 100)
Set HEADNODE to signify the head of the path.
Set FORCE if you want un-fightable gravity ( i.e. can't go backwards )
Set PULL_UPWARDS if you want the gravnodes to pull you upwards also
******************************************************************************/
#define PULL_UPWARDS ( 1 << 2 )
Event EV_GravPath_Create
(
"gravpath_create",
EV_SCRIPTONLY,
NULL,
NULL,
"Create the grav path."
);
Event EV_GravPath_Activate
(
"activate",
EV_SCRIPTONLY,
NULL,
NULL,
"Activate the grav path."
);
Event EV_GravPath_Deactivate
(
"deactivate",
EV_SCRIPTONLY,
NULL,
NULL,
"Deactivate the grav path."
);
Event EV_GravPath_SetSpeed
(
"speed",
EV_DEFAULT,
"f",
"speed",
"Set the speed of the grav path."
);
Event EV_GravPath_SetMaxSpeed
(
"maxspeed",
EV_SCRIPTONLY,
"f",
"maxspeed",
"Set the max speed of the grav path."
);
Event EV_GravPath_SetRadius
(
"radius",
EV_SCRIPTONLY,
"f",
"radius",
"Set the radius of the grav path."
);
CLASS_DECLARATION( Entity, GravPathNode, "info_grav_pathnode" )
{
{ &EV_GravPath_Create, &GravPathNode::CreatePath },
{ &EV_GravPath_Activate, &GravPathNode::Activate },
{ &EV_GravPath_Deactivate, &GravPathNode::Deactivate },
{ &EV_GravPath_SetSpeed, &GravPathNode::SetSpeed },
{ &EV_GravPath_SetMaxSpeed, &GravPathNode::SetMaxSpeed },
{ &EV_GravPath_SetRadius, &GravPathNode::SetRadius },
{ NULL, NULL }
};
GravPathNode::GravPathNode()
{
if ( LoadingSavegame )
{
// all data will be setup by the archive function
return;
}
setMoveType( MOVETYPE_NONE );
setSolidType( SOLID_NOT );
hideModel();
speed = 100.0f;
max_speed = 200.0f;
radius = 256.0f;
headnode = spawnflags & 1;
active = true;
// This is the head of a new path, post an event to create the path
if ( headnode )
{
PostEvent( EV_GravPath_Create, 0.0f );
}
}
void GravPathNode::SetSpeed( Event *ev )
{
speed = ev->GetFloat( 1 );
}
void GravPathNode::SetMaxSpeed( Event *ev )
{
max_speed = ev->GetFloat( 1 );
}
void GravPathNode::SetRadius( Event *ev )
{
radius = ev->GetFloat( 1 );
}
float GravPathNode::Speed( void )
{
if ( active )
return speed;
else
return 0;
}
float GravPathNode::MaxSpeed( void )
{
return max_speed;
}
void GravPathNode::Activate( Event *ev )
{
GravPathNode *node;
Entity *ent;
const char *target;
active = true;
node = this;
// Go through the entire path and activate it
target = node->Target();
while (target[0])
{
ent = G_FindTarget( NULL, target );
if ( ent )
{
node = (GravPathNode *)ent;
assert( node );
node->active = true;
}
else
{
gi.Error( ERR_DROP, "GravPathNode::CreatePath: target %s not found\n",target);
}
target = node->Target();
}
}
void GravPathNode::Deactivate(Event *ev)
{
GravPathNode *node;
Entity *ent;
const char *target;
active = false;
node = this;
// Go through the entire path and activate it
target = node->Target();
while (target[0])
{
ent = G_FindTarget( NULL, target);
if ( ent )
{
node = (GravPathNode *)ent;
assert( node );
node->active = false;
}
else
{
gi.Error( ERR_DROP, "GravPathNode::CreatePath: target %s not found\n",target);
}
target = node->Target();
}
}
void GravPathNode::CreatePath(Event *ev)
{
const char *target;
GravPath *path = new GravPath;
GravPathNode *node;
Entity *ent;
ClearBounds( path->mins, path->maxs );
// This node is the head of a path, create a new path in the path manager.
// and add it in, then add all of it's children in the path.
node = this;
path->AddNode(node);
path->force = spawnflags & 2;
// Make the path from the targetlist.
target = node->Target();
while (target[0])
{
ent = G_FindTarget( NULL, target );
if ( ent )
{
node = (GravPathNode *)ent;
assert( node );
path->AddNode(node);
}
else
{
gi.Error( ERR_DROP, "GravPathNode::CreatePath: target %s not found\n",target);
}
target = node->Target();
}
// Set the origin.
path->origin = path->mins + path->maxs;
path->origin *= 0.5f;
}
CLASS_DECLARATION( Listener, GravPath, NULL )
{
{ NULL, NULL }
};
GravPath::GravPath()
{
pathlength = 0;
from = NULL;
to = NULL;
nextnode = 1;
force = 0;
if ( !LoadingSavegame )
{
gravPathManager.AddPath(this);
}
}
GravPath::~GravPath()
{
pathlength = 0;
from = NULL;
to = NULL;
nextnode = 1;
gravPathManager.RemovePath(this);
}
void GravPath::Clear( void )
{
nextnode = 1;
pathlength = 0;
from = NULL;
to = NULL;
pathlist.FreeObjectList();
}
void GravPath::Reset( void )
{
nextnode = 1;
}
GravPathNode *GravPath::Start( void )
{
return from;
}
GravPathNode *GravPath::End( void )
{
return to;
}
void GravPath::AddNode( GravPathNode *node )
{
int num;
Vector r,addp;
if ( !from )
{
from = node;
}
to = node;
pathlist.AddObject( GravPathNodePtr( node ) );
num = NumNodes();
if ( num > 1 )
{
pathlength += ( node->origin - GetNode( num )->origin ).length();
}
r.setXYZ(node->Radius(),node->Radius(),node->Radius());
addp = node->origin + r;
AddPointToBounds(addp,mins,maxs);
addp = node->origin - r;
AddPointToBounds(addp,mins,maxs);
}
GravPathNode *GravPath::GetNode( int num )
{
return pathlist.ObjectAt( num );
}
GravPathNode *GravPath::NextNode( void )
{
if ( nextnode <= NumNodes() )
{
return pathlist.ObjectAt( nextnode++ );
}
return NULL;
}
Vector GravPath::ClosestPointOnPath( const Vector &pos, Entity &ent, float *ret_dist, float *speed, float *radius )
{
GravPathNode *s;
GravPathNode *e;
int num;
int i;
float bestdist;
Vector bestpoint;
float dist;
float segmentlength;
Vector delta;
Vector p1;
Vector p2;
Vector p3;
float t;
//trace_t trace;
num = NumNodes();
s = GetNode( 1 );
//trace = G_Trace( pos, ent.mins, ent.maxs, s->origin, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 1" );
bestpoint = s->origin;
delta = bestpoint - pos;
bestdist = delta.length();
*speed = s->Speed();
*radius = s->Radius();
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
// check if we're closest to the endpoint
delta = e->origin - pos;
dist = delta.length();
if ( dist < bestdist )
{
//trace = G_Trace( pos, ent.mins, ent.maxs, e->origin, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 2" );
bestdist = dist;
bestpoint = e->origin;
*speed = e->Speed();
*radius = e->Radius();
}
// check if we're closest to the segment
p1 = e->origin - s->origin;
segmentlength = p1.length();
p1 *= 1.0f / segmentlength;
p2 = pos - s->origin;
t = p1 * p2;
if ( ( t > 0.0f ) && ( t < segmentlength ) )
{
p3 = ( p1 * t ) + s->origin;
delta = p3 - pos;
dist = delta.length();
if ( dist < bestdist )
{
//trace = G_Trace( pos, ent.mins, ent.maxs, p3, &ent, MASK_PLAYERSOLID, false, "GravPath::ClosestPointOnPath 3" );
bestdist = dist;
bestpoint = p3;
*speed = (e->Speed() * t) + (s->Speed() * (1.0f - t));
*radius = (e->Radius() * t) + (s->Radius() * (1.0f - t));
}
}
s = e;
}
*ret_dist = bestdist;
return bestpoint;
}
float GravPath::DistanceAlongPath( const Vector &pos, float *speed )
{
GravPathNode *s;
GravPathNode *e;
int num;
int i;
float bestdist;
float dist;
float segmentlength;
Vector delta;
Vector segment;
Vector p1;
Vector p2;
Vector p3;
float t;
float pathdist;
float bestdistalongpath;
float oosl;
pathdist = 0;
num = NumNodes();
s = GetNode( 1 );
delta = s->origin - pos;
bestdist = delta.length();
bestdistalongpath = 0;
*speed = s->Speed();
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
segment = e->origin - s->origin;
segmentlength = segment.length();
// check if we're closest to the endpoint
delta = e->origin - pos;
dist = delta.length();
if ( dist < bestdist )
{
bestdist = dist;
bestdistalongpath = pathdist + segmentlength;
*speed = e->Speed();
}
// check if we're closest to the segment
oosl = ( 1.0f / segmentlength );
p1 = segment * oosl;
p1.normalize();
p2 = pos - s->origin;
t = p1 * p2;
if ( ( t > 0.0f ) && ( t < segmentlength ) )
{
p3 = ( p1 * t ) + s->origin;
delta = p3 - pos;
dist = delta.length();
if ( dist < bestdist )
{
bestdist = dist;
bestdistalongpath = pathdist + t;
t *= oosl;
*speed = (e->Speed() * t) + (s->Speed() * (1.0f - t));
}
}
s = e;
pathdist += segmentlength;
}
return bestdistalongpath;
}
Vector GravPath::PointAtDistance( const Vector &pos, float dist, qboolean is_player, float *max_speed )
{
GravPathNode *s;
GravPathNode *e;
int num;
int i;
Vector delta;
Vector p1;
float t;
float pathdist;
float segmentlength;
num = NumNodes();
s = GetNode( 1 );
pathdist = 0;
for( i = 2; i <= num; i++ )
{
e = GetNode( i );
delta = e->origin - s->origin;
segmentlength = delta.length();
if ( ( pathdist + segmentlength ) > dist )
{
t = dist - pathdist;
p1 = delta * ( t / segmentlength );
// return p1 + s->origin;
if ( e->spawnflags & PULL_UPWARDS && is_player )
p1.z = p1.length() / 2.0f;
*max_speed = e->MaxSpeed();
return p1 + pos;
}
s = e;
pathdist += segmentlength;
}
*max_speed = s->MaxSpeed();
// cap it off at start or end of path
return s->origin;
}
void GravPath::DrawPath( float r, float g, float b )
{
Vector s;
Vector e;
Vector offset;
GravPathNode *node;
int num;
int i;
num = NumNodes();
node = GetNode( 1 );
s = node->origin;
G_DebugBBox( s, Vector(8.0f, 8.0f, 8.0f), Vector(-8.0f, -8.0f, -8.0f), 0.0f, 1.0f, 0.0f, 1.0f );
offset = ( Vector( r, g, b ) * 4.0f ) + Vector( 0.0f, 0.0f, 0.0f );
offset = Vector( 0.0f, 0.0f, 0.0f );
for( i = 2; i <= num; i++ )
{
node = GetNode( i );
e = node->origin;
G_DebugLine( s + offset, e + offset, r, g, b, 1 );
G_DebugBBox( e, Vector( 8.0f, 8.0f, 8.0f ), Vector( -8.0f, -8.0f, -8.0f ), 0.0f, 1.0f, 0.0f, 1.0f );
s = e;
}
G_DebugBBox( origin,mins-origin,maxs-origin, 1.0f, 0.0f, 0.0f, 1.0f );
}
int GravPath::NumNodes( void )
{
return pathlist.NumObjects();
}
float GravPath::Length( void )
{
return pathlength;
}