sin-2015/ceilingsteering.cpp
1999-04-22 00:00:00 +00:00

499 lines
10 KiB
C++

/*
================================================================
CEILING STEERING FOR CRAWLER
================================================================
Copyright (C) 1998 by 2015, Inc.
All rights reserved.
This source is may not be distributed and/or modified without
expressly written permission by 2015, Inc.
*/
#include "g_local.h"
#include "ceilingsteering.h"
#include "actor.h"
/****************************************************************************
CeilingObstacleAvoidance Class Definition
****************************************************************************/
CLASS_DECLARATION( Steering, CeilingObstacleAvoidance, NULL );
ResponseDef CeilingObstacleAvoidance::Responses[] =
{
{ NULL, NULL }
};
CeilingObstacleAvoidance::CeilingObstacleAvoidance()
{
avoidwalls = true;
}
void CeilingObstacleAvoidance::AvoidWalls (qboolean avoid)
{
avoidwalls = avoid;
}
void CeilingObstacleAvoidance::ShowInfo (Actor &self)
{
Steering::ShowInfo( self );
gi.printf( "\navoidwalls: %d\n", avoidwalls );
}
qboolean CeilingObstacleAvoidance::Evaluate (Actor &self)
{
Vector predictedposition;
Vector normal;
Vector angles;
Vector dir;
Vector right;
Vector delta;
float urgency;
float dot;
trace_t tracef;
Entity *ent;
ResetForces();
angles = self.movedir.toAngles();
angles.AngleVectors( NULL, &right, NULL );
origin = self.worldorigin;
origin.z -= 1;
predictedposition = origin + self.movedir * self.movespeed;//maxspeed;
tracef = G_Trace( origin, self.mins, self.maxs, predictedposition, &self, self.edict->clipmask, "CeilingObstacleAvoidance forward" );
if ( tracef.fraction < 1 )
{
urgency = 1.0 - tracef.fraction;
normal = tracef.plane.normal;
ent = tracef.ent->entity;
if ( ent->getSolidType() != SOLID_BSP )
{
dot = -( right * ( ent->worldorigin - self.worldorigin ) );
}
else
{
if ( !avoidwalls )
{
return true;
}
dot = right * normal;
}
if ( dot < 0 )
{
// turn left
steeringforce = Vector( 0, 90, 0 );;
}
else
{
// turn right
steeringforce = Vector( 0, -90, 0 );;
}
}
else
{
return true;
}
steeringforce *= urgency;
return true;
}
/****************************************************************************
CeilingObstacleAvoidance2 Class Definition
****************************************************************************/
CLASS_DECLARATION( Steering, CeilingObstacleAvoidance2, NULL );
ResponseDef CeilingObstacleAvoidance2::Responses[] =
{
{ NULL, NULL }
};
CeilingObstacleAvoidance2::CeilingObstacleAvoidance2()
{
avoidwalls = true;
}
void CeilingObstacleAvoidance2::AvoidWalls (qboolean avoid)
{
avoidwalls = avoid;
}
void CeilingObstacleAvoidance2::ShowInfo (Actor &self)
{
Steering::ShowInfo( self );
gi.printf( "\navoidwalls: %d\n", avoidwalls );
}
qboolean CeilingObstacleAvoidance2::Evaluate (Actor &self)
{
Vector predictedposition;
Vector normal;
Vector angles;
Vector dir;
Vector right;
Vector delta;
float urgency;
float dot;
trace_t tracef;
Entity *ent;
ResetForces();
angles = self.movedir.toAngles();
angles.AngleVectors( NULL, &right, NULL );
origin = self.worldorigin;
origin.z -= 1;
predictedposition = origin + self.movedir * self.movespeed;//maxspeed;
tracef = G_Trace( origin, self.mins, self.maxs, predictedposition, &self, self.edict->clipmask, "ObstacleAvoidance2 forward" );
if ( tracef.fraction < 1 )
{
urgency = 1.0 - tracef.fraction;
normal = tracef.plane.normal;
ent = tracef.ent->entity;
if ( ent->getSolidType() != SOLID_BSP )
{
dot = -( right * ( ent->worldorigin - self.worldorigin ) );
}
else
{
if ( !avoidwalls )
{
return true;
}
dot = right * normal;
}
if ( dot < 0 )
{
// turn left
steeringforce = Vector( 0, 22, 0 ) * urgency;
}
else
{
// turn right
steeringforce = Vector( 0, 22, 0 ) * urgency;
}
}
else
{
return true;
}
steeringforce *= urgency;
return true;
}
/****************************************************************************
CeilingChase Class Definition
****************************************************************************/
CLASS_DECLARATION( Steering, CeilingChase, NULL );
ResponseDef CeilingChase::Responses[] =
{
{ NULL, NULL }
};
CeilingChase::CeilingChase()
{
goalent = NULL;
goal = vec_zero;
usegoal = false;
}
void CeilingChase::SetGoalPos (Vector goalpos)
{
goal = goalpos;
usegoal = true;
goalent = NULL;
}
void CeilingChase::SetTarget (Entity *ent)
{
goalent = ent;
usegoal = false;
}
void CeilingChase::ShowInfo (Actor &self)
{
Steering::ShowInfo( self );
gi.printf( "\nseek:\n" );
seek.ShowInfo( self );
gi.printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z );
if ( goalent )
{
gi.printf( "\ngoalent: #%d '%s'\n", goalent->entnum, goalent->targetname.c_str() );
}
else
{
gi.printf( "\ngoalent: NULL\n" );
}
gi.printf( "avoid:\n" );
avoid.ShowInfo( self );
gi.printf( "\ntime: %f\n", avoidtime );
gi.printf( "usegoal: %d\n", usegoal );
gi.printf( "wander: %d\n", wander );
gi.printf( "stuck: %d\n", stuck );
gi.printf( "avoidvec : ( %f, %f, %f )\n", avoidvec.x, avoidvec.y, avoidvec.z );
}
void CeilingChase::Begin (Actor &self)
{
seek.Begin( self );
avoid.AvoidWalls( false );
avoid.Begin( self );
turnto.Begin( self );
anim = self.animname;
stuck = 0;
wander = 0;
}
Vector CeilingChase::ChooseRandomDirection (Actor &self)
{
Vector dir;
Vector ang;
Vector bestdir;
float bestfraction;
trace_t trace;
trace_t groundtrace;
int i;
int j;
int t;
int u;
Vector s;
Vector start;
Vector end;
Vector groundend;
s = Vector( 0, 0, -STEPSIZE );
start = self.worldorigin + s;
// quantize to nearest 45 degree
u = ( ( int )( self.worldangles.y * ( 1 / 45 ) + 22.5 ) ) * 45;
bestfraction = -1;
//
// in case we don't find anything!
//
bestdir = self.worldorigin - ( Vector( self.orientation[ 0 ] ) * 100 );
for( i = 0; i <= 180; i += 20 )
{
if ( rand() < 0.3 )
{
i += 20;
}
t = i;
if ( rand() < 0.5 )
{
// sometimes we choose left first, other times right.
t = -t;
}
for( j = -1; j < 2; j += 2 )
{
if ( ( j == 1 ) && ( i == 180 ) )
{
ang.y = self.worldangles.y + ( t * j );
}
else
{
ang.y = u + t * j;
}
ang.AngleVectors( &dir, NULL, NULL );
end = self.worldorigin + dir * 140 + s;
trace = G_Trace( start, self.mins, self.maxs, end, &self,
self.edict->clipmask, "CeilingChase::ChooseRandomDirection 1" );
if ( ( trace.fraction > bestfraction ) && ( !trace.startsolid ) && !( trace.allsolid ) )
{
if ( trace.endpos != avoidvec )
{
// check if we're near the ground
end = self.worldorigin + dir * 32 + s;
groundend = end;
groundend.z += STEPSIZE * 2;
groundtrace = G_Trace( end, self.mins, self.maxs, groundend, &self,
self.edict->clipmask, "CeilingChase::ChooseRandomDirection 2" );
if ( groundtrace.fraction != 1 )
{
bestdir = trace.endpos;
bestfraction = trace.fraction;
}
}
}
if ( i == 0 )
{
break;
}
}
}
return bestdir;
}
qboolean CeilingChase::Evaluate (Actor &self)
{
qboolean result;
trace_t trace;
if ( !usegoal && ( !goalent || goalent->deadflag ) )
{
return false;
}
ResetForces();
if ( !wander )
{
if ( self.lastmove == STEPMOVE_OK )
{
stuck = 0;
}
else
{
stuck++;
if ( stuck >= 2 )
{
stuck = 3;
wander = 1;
}
}
}
switch( wander )
{
case 1 :
stuck--;
if ( !stuck )
{
wander = 0;
break;
}
wanderstart = self.worldorigin;
avoidvec = ChooseRandomDirection( self );
wandertime = level.time + 1;
wander = 2;
case 2 :
seek.SetTargetPosition( avoidvec );
seek.SetTargetVelocity( vec_zero );
seek.SetPosition( self.worldorigin );
seek.SetDir( self.movedir );
seek.SetMaxSpeed( self.movespeed );
result = seek.Evaluate( self );
if ( result )
{
if ( ( level.time > wandertime ) && ( self.lastmove != STEPMOVE_OK ) )
{
wander = 0;
stuck = 0;
}
self.Accelerate( seek.steeringforce );
return true;
}
wander = 0;
break;
turnto.SetDirection( ( wanderstart - self.worldorigin ).toYaw() );
wander = 3;
wandertime = level.time + 1;
case 3 :
if ( level.time < wandertime )
{
turnto.Evaluate( self );
self.Accelerate( turnto.steeringforce );
return true;
}
wander = 0;
break;
}
if ( goalent && ( goalent->edict->solid != SOLID_NOT ) && ( goalent->edict->solid != SOLID_TRIGGER ) )
{
trace = G_Trace( self.worldorigin, self.mins, self.maxs, self.worldorigin +
Vector( self.orientation[ 0 ] ) * self.movespeed * 0.1, &self, self.edict->clipmask, "CeilingChase" );
if ( trace.ent->entity == goalent )
{
return false;
}
}
if ( goalent )
{
seek.SetTargetPosition( goalent->worldorigin );
seek.SetTargetVelocity( goalent->velocity );
}
else
{
seek.SetTargetPosition( goal );
seek.SetTargetVelocity( vec_zero );
}
seek.SetPosition( self.worldorigin );
seek.SetDir( self.movedir );
seek.SetMaxSpeed( self.movespeed );
result = seek.Evaluate( self );
steeringforce = seek.steeringforce;
if ( !result )
{
return false;
}
if ( avoidtime < level.time )
{
avoid.SetMaxSpeed( self.movespeed );
avoid.SetPosition( self.worldorigin );
avoid.SetDir( self.movedir );
avoid.Evaluate( self );
if ( avoid.steeringforce == vec_zero )
{
avoidtime = level.time + 0.1;
}
else
{
steeringforce += avoid.steeringforce;
}
}
self.Accelerate( steeringforce );
return true;
}
void CeilingChase::End (Actor &self)
{
seek.End( self );
avoid.End( self );
turnto.End( self );
}