/* ================================================================ 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 ); }