⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 navigate.cpp

📁 this keik game source
💻 CPP
📖 第 1 页 / 共 4 页
字号:
            Child[ i ].door = door->entnum;
            }
         else
            {
            Child[ i ].door = 0;
            }
         }
		}
	}

EXPORT_FROM_DLL void PathNode::Unarchive
	(
	Archiver &arc
	)

	{
	int i;

	nodenum = arc.ReadInteger();
	assert( nodenum <= MAX_PATHNODES );
	if ( nodenum > MAX_PATHNODES )
		{
		arc.FileError( "Node exceeds max path nodes" );
		}

	nodeflags = arc.ReadInteger();

	setOrigin( arc.ReadVector() );
	setAngles( arc.ReadVector() );

	setangles	= arc.ReadBoolean();
	target	   = arc.ReadString();
	targetname	= arc.ReadString();
	animname		= arc.ReadString();

   occupiedTime = arc.ReadFloat();
   entnum       = arc.ReadInteger();
   if ( !LoadingSavegame )
      {
      occupiedTime = 0;
      entnum       = 0;
      }

	numChildren = arc.ReadInteger();
	assert( numChildren <= NUM_PATHSPERNODE );
	if ( numChildren > NUM_PATHSPERNODE )
		{
		arc.FileError( "Exceeded num paths per node" );
		}

	for( i = 0; i < numChildren; i++ )
		{
		Child[ i ].node		= arc.ReadShort();
		Child[ i ].moveCost	= arc.ReadShort();
		arc.ReadRaw( Child[ i ].maxheight, sizeof( Child[ i ].maxheight ) );
		Child[ i ].door		= arc.ReadInteger();
		}

   if ( !LoadingSavegame )
      {
      // Fixup the doors
      PostEvent( EV_Path_FindEntities, 0 );
      }

	pathnodes[ nodenum ] = this;
	if ( ai_maxnode < nodenum )
		{
		ai_maxnode = nodenum;
		}

	PathManager.AddNode( this );
	}

EXPORT_FROM_DLL void RestoreEnts
	(
	void
	)

	{
	int i;

	for( i = 0; i < NumIgnoreObjects; i++ )
		{
		IgnoreObjects[ i ]->link();
		}
	}

EXPORT_FROM_DLL qboolean PathNode::TestMove
	(
   Entity *ent,
   Vector start,
   Vector end,
	Vector &min,
	Vector &max,
	qboolean allowdoors,
   qboolean fulltest
	)

	{
   // NOTE: TestMove may allow wide paths to succeed when they shouldn't since it 
   // may place the lower node above obstacles that actors can't step over.
   // Since any path that's wide enough for large boxes must also allow
   // thinner boxes to go through, you must ignore the results of TestMove
   // when thinner checks have already failed.
	trace_t  trace;
	Vector   end_trace;
   Vector   pos;
   Vector   dir;
   float    t;
   float    dist;

   // By requiring that paths have STEPSIZE headroom above the path, we simplify the test
   // to see if an actor can move to a node down to a simple trace.  By stepping up the start
   // and end points, we account for the actor's ability to step up any geometry lower than
   // STEPSIZE in height.
   start.z += STEPSIZE;
   end.z += STEPSIZE;

   // Check the move
   trace = G_Trace( start, min, max, end, ent, MASK_PATHSOLID, "PathNode::TestMove 1" );
   if ( trace.startsolid || ( trace.fraction != 1 ) )
		{
      // No direct path available.  The actor will most likely not be able to move through here.
      return false;
		}

   if ( !fulltest )
      {
      // Since we're not doing a full test (full tests are only done when we're connecting nodes to save time),
      // we test to see if the midpoint of the move would only cause a change in height of STEPSIZE
      // from the predicted height.  This prevents most cases where a dropoff lies between a actor and a node.
	   Vector pos;
	   
      // Since we start and end are already STEPSIZE above the ground, we have to check twice STEPSIZE below
      // the midpoint to see if the midpoint is on the ground.
      dir = end - start;
      pos = start + dir * 0.5;
	   end_trace = pos;
      end_trace.z -= STEPSIZE * 2;

	   // Check that the midpos is onground.  This may fail on ok moves, but a true test would be too slow 
      // to do in real time.  Also, we may miss cases where a dropoff exists before or after the midpoint.
      // Once the actor is close enough to the drop off, it will discover the fall and hopefully try
      // another route.
	   trace = G_Trace( pos, min, max, end_trace, ent, MASK_PATHSOLID, "PathNode::TestMove 2" );
      if ( trace.startsolid || ( trace.fraction == 1 ) )
		   {
         // We're not on the ground, so there's a good posibility that we can't make this move without falling.
		   return false;
		   }
      }
   else if ( !( contents & MASK_WATER ) )
      {
      // When we're creating the paths during load time, we do a much more exhaustive test to see if the
      // path is valid.  This test takes a bounding box and moves it 8 units at a time closer to the goal,
      // testing the ground after each move.  The test involves checking whether we will fall more than
      // STEPSIZE to the ground (since we've raised the start and end points STEPSIZE above the ground,
      // we must actually test 2 * STEPSIZE down to see if we're on the ground).  After each test, we set
      // the new height of the box to be STEPSIZE above the ground.  Each move closer to the goal is only
      // done horizontally to simulate how the actors normally move.  This method ensures that any actor
      // wider than 8 units in X and Y will be able to move from start to end.
      //
      // NOTE: This may allow wide paths to succeed when they shouldn't since it 
      // may place the lower node above obstacles that actors can't step over.
      // Since any path that's wide enough for large boxes must also allow
      // thinner boxes to go through, you must ignore the results of TestMove
      // when thinner checks have already failed.

      dir = end - start;
	   dir.z = 0;
	   dist = dir.length();
      dir *= 1 / dist;

	   // check the entire move
	   pos = start;
	   for( t = 0; t < dist; t += 8 )
		   {
         // Move the box to our position along the path and make our downward trace vector
		   end_trace.x = pos.x = start.x + t * dir.x;
		   end_trace.y = pos.y = start.y + t * dir.y;
         end_trace.z = pos.z - STEPSIZE * 2;

         // check the ground
		   trace = G_Trace( pos, min, max, end_trace, ent, MASK_PATHSOLID, "PathNode::TestMove 3" );
		   if ( trace.startsolid || ( trace.fraction == 1 ) )
			   {
            // Either we're stuck in something solid, or we would fall farther than STEPSIZE to the ground,
            // so the path is not acceptable.
            return false;
			   }

         // move the box to STEPSIZE above the ground.
		   pos.z = trace.endpos[ 2 ] + STEPSIZE;
		   }
      }

   return true;
	}

EXPORT_FROM_DLL qboolean PathNode::CheckMove
	(
   Entity *ent,
   Vector pos,
	Vector &min,
	Vector &max,
	qboolean allowdoors,
   qboolean fulltest
	)

   {
   // Since we need to support actors of variable widths, we need to do some special checks when a potential
   // path goes up or down stairs.  Placed pathnodes are only 16x16 in width, so when they are dropped to the
   // ground, they may end in a position where a larger box would not fit.  Making the pathnodes larger
   // would make it hard to place paths where smaller actors could go, and making paths of various sizes would
   // be overkill (more work for the level designer, or a lot of redundant data).  The solution is to treat
   // paths with verticle movement differently than paths that are purely horizontal.  For horizontal moves,
   // a simple trace STEPSIZE above the ground will be sufficient to prove that we can travel from one node
   // to another, in either direction.  For moves that have some change in height, we can check that we have
   // a clear path by tracing horizontally from the higher node to a point where larger bounding box actors
   // could then move at a slope downward to the lower node.  This fixes the problem where path points that
   // are larger than the depth of a step would have to intersect with the step in order to get the center
   // of the box on solid ground.  If you're still confused, well, tough. :)  Think about the problem of
   // larger bounding boxes going up stairs for a bit and you should see the problem.  You can also read
   // section 8.4, "Translating a Convex Polygon", from Computational Geometry in C (O'Rourke) (a f'ing
   // great book, BTW) for information on similar problems (which is also a good explanation of how
   // Quake's collision detection works).
   trace_t trace;
   int height;

   height = ( int )fabs( pos.z - worldorigin.z );
   // Check if the path is strictly horizontal
   if ( !height )
      {
      // We do two traces for the strictly horizontal test.  One normal, and one STEPSIZE
      // above.  The normal trace is needed because most doors in the game aren't tall enough
      // to allow actors to trace STEPSIZE above the ground.  This means that failed horizontal
      // tests require two traces.  Annoying.
      trace = G_Trace( worldorigin, min, max, pos, ent, MASK_PATHSOLID, "PathNode::CheckMove 1" );
      if ( !trace.startsolid && ( trace.fraction == 1 ) )
		   {
         return true;
		   }

      // Do the step test
      return TestMove( ent, pos, worldorigin, min, max, allowdoors, fulltest );
      }
   
   Vector size;
   float  width;

   size = max - min;
   width = max( size.x, size.y );

   // if our bounding box is smaller than that of the pathnode, we can do the standard trace.
   if ( width <= 32 )
      {
      return TestMove( ent, pos, worldorigin, min, max, allowdoors, fulltest );
      }

   Vector start;
   Vector end;
   Vector delta;
   float  radius;
   float  len;

   // We calculate the radius of the smallest cylinder that would contain the bounding box.
   // In some cases, this would make the first horizontal move longer than it needs to be, but
   // that shouldn't be a problem.

   // multiply the width by 1/2 the square root of 2 to get radius
   radius = width * 1.415 * 0.5;

   // Make sure that our starting position is the higher node since it doesn't matter which
   // direction the move is in.
   if ( pos.z < worldorigin.z )
      {
      start = worldorigin;
      end = pos;
      }
   else
      {
      start = pos;
      end = worldorigin;
      }

   // If the distance between the two points is less than the radius of the bounding box,
   // then we only have to do the horizontal test since larger bounding boxes would not fall.
   delta = end - start;
   len = delta.length();
   if ( len <= radius )
      {
      end.z = start.z;
      return TestMove( ent, start, end, min, max, allowdoors, fulltest );
      }

   Vector mid;

   // normalize delta and multiply by radius (saving a few multiplies by combining into one).
   delta *= radius / len;

   mid = start;
   mid.x += delta.x;
   mid.y += delta.y;

   // Check the horizontal move
   if ( !TestMove( ent, start, mid, min, max, allowdoors, fulltest ) )
      {
      return false;
      }

   // Calculate our new endpoint
   end.z -= delta.z;

   // Check our new sloping move
   return TestMove( ent, mid, end, min, max, allowdoors, fulltest );
   }

EXPORT_FROM_DLL Door *PathNode::CheckDoor
	(
	Vector pos
	)

	{
	trace_t	trace;
	Entity	*ent;

	trace = G_Trace( worldorigin, vec_zero, vec_zero, pos, NULL, MASK_PATHSOLID, "PathNode::CheckDoor" );

	ent = trace.ent->entity;
	if ( ent && ent->isSubclassOf( Door ) )
		{
		return ( Door * )ent;
		}

	return NULL;
	}

EXPORT_FROM_DLL qboolean PathNode::CheckMove
	(
	Vector pos,
	Vector min,
	Vector max
	)

	{
	return true;
	}

EXPORT_FROM_DLL qboolean PathNode::CheckPath
	(
	PathNode *node,
	Vector min,
	Vector max,
   qboolean fulltest
	)

	{
	Vector	delta;
	qboolean allowdoors;
   qboolean result;

	delta = node->worldorigin - worldorigin;
	delta[ 2 ] = 0;
	if ( delta.length() >= PATHMAP_CELLSIZE )
		{
		return false;
		}

	allowdoors = ( nodeflags & AI_DOOR ) && ( node->nodeflags & AI_DOOR );

   result = CheckMove( NULL, node->worldorigin, min, max, allowdoors, fulltest );
	RestoreEnts();

   return result;
	}

EXPORT_FROM_DLL qboolean PathNode::ClearPathTo
	(
	PathNode *node,
	byte maxheight[ NUM_WIDTH_VALUES ],
   qboolean fulltest
	)

	{
	int      i;
	int      width;
   int      height;
   int      bottom;
   int      top;
	Vector   min;
	Vector   max;
   Vector   bmin;
   Vector   bmax;
	qboolean path;
	edict_t	*touch[ MAX_EDICTS ];
   Entity   *ent;
   int      num;

	path = false;
	for( i = 0; i < NUM_WIDTH_VALUES; i++ )
		{
		maxheight[ i ] = 0;
      }

   width = NUM_WIDTH_VALUES * WIDTH_STEP * 0.5;
   min = Vector( -width, -width, 0 );
   max = Vector( width, width, MAX_HEIGHT );
   G_CalcBoundsOfMove( worldorigin, node->worldorigin, min, max, &bmin, &bmax );

  	num = gi.BoxEdicts( bmin.vec3(), bmax.vec3(), touch, MAX_EDICTS, AREA_SOLID );
   for( i = 0; i < num; i++ )
      {
		ent = touch[ i ]->entity;
		if ( ent && ent->isSubclassOf( Door ) )
         {
         ent->unlink();
         }
      }

	for( i = 0; i < NUM_WIDTH_VALUES; i++ )
		{
		width = ( i + 1 ) * WIDTH_STEP * 0.5;

		min.x = min.y = -width;
      max.x = max.y = width;

      // Perform a binary search to find the height of the path.  Neat, eh? :)
      bottom = 0;
      top = MAX_HEIGHT;
      while( top >= bottom )
         {
         height = ( ( bottom + top + 3 ) >> 1 ) & ~0x3;
         if ( !height )
            {
            break;
            }

			max.z = ( float )height;
			if ( !CheckPath( node, min, max, fulltest ) )
            {
            top = height - 4;
            }
         else
            {
            bottom = height + 4;
      	   maxheight[ i ] = height;
            }
         }

      if ( !maxheight[ i ] )
         {
         // If no paths were available at this width, don't allow any wider paths.
         // CheckPath uses TestMove which may allow wide paths to succeed when they
         // shouldn't since it may place the lower node above obstacles that actors
         // can't step over.  Since any path that's wide enough for large boxes must
         // also allow thinner boxes to go through, this check avoids the hole in
         // TestMove's functioning.
         break;
         }

      path = true;
      }

   // Restore the doors
   for( i = 0; i < num; i++ )
      {
		ent = touch[ i ]->entity;
		if ( ent && ent->isSubclassOf( Door ) )
         {
         ent->link();
         }
      }

	return path;
	}

EXPORT_FROM_DLL qboolean PathNode::LadderTo
	(
	PathNode *node,
	byte maxheight[ NUM_WIDTH_VALUES ]
	)

	{
	int i;
	int j;
	int m;
	int width;
	Vector min;
	Vector max;
	qboolean path;

	trace_t trace;
	
	if ( !( nodeflags & AI_LADDER ) || !( node->nodeflags & AI_LADDER ) )
		{
		return false;
		}

	if ( ( worldorigin.x != node->worldorigin.x ) || ( worldorigin.y != node->worldorigin.y ) )
		{
		return false;
		}

	path = false;

	for( i = 0; i < NUM_WIDTH_VALUES; i++ )
		{
		width = ( i + 1 ) * WIDTH_STEP * 0.5;
		min = Vector( -width, -width, 12 );
		max = Vector( width, width, 40 );

		trace = G_Trace( worldorigin, min, max, node->worldorigin, NULL, MASK_PATHSOLID, "PathNode::LadderTo 1" );
		if ( ( trace.fraction != 1 ) || trace.startsolid )
			{
			maxheight[ i ] = 0;
			continue;
			}

		path = true;

		m = 40;
		for( j = 48; j < MAX_HEIGHT; j+= 8 )
			{
			max.z = j;
			trace = G_Trace( worldorigin, min, max, node->worldorigin, NULL, MASK_PATHSOLID, "PathNode::LadderTo 2" );
			if ( ( trace.fraction != 1 ) || trace.startsolid )
				{
				break;
				}
			
			m = j;
			}

		maxheight[ i ] = m;
		}

	return path;
	}

EXPORT_FROM_DLL qboolean PathNode::ConnectedTo
	(
	PathNode *node
	)

	{
	int i;

	for( i = 0; i < numChildren; i++ )
		{
		if ( Child[ i ].node == node->nodenum )
			{
			return true;
			}
		}

	return false;
	}

EXPORT_FROM_DLL void PathNode::ConnectTo
	(
	PathNode *node,
	byte maxheight[ NUM_WIDTH_VALUES ],
	float cost,
	Door *door
	)

	{
	int i;

	if ( ( numChildren < NUM_PATHSPERNODE ) && ( node != this ) )
		{
		Child[ numChildren ].node = node->nodenum;
		for( i = 0; i < NUM_WIDTH_VALUES; i++ )
			{
			Child[ numChildren ].maxheight[ i ] = maxheight[ i ];
			}
		Child[ numChildren ].moveCost = ( int )cost;
		Child[ numChildren ].door = door ? door->entnum : 0;
		numChildren++;
		}
	else
		{
		warning( "ConnectTo", "Child overflow" );
		}
	}

EXPORT_FROM_DLL void PathNode::ConnectTo
	(
	PathNode *node,
	byte maxheight[ NUM_WIDTH_VALUES ]
	)

	{
	Vector delta;
	Door *door;

	door = CheckDoor( node->worldorigin );
	delta = node->worldorigin - worldorigin;
	ConnectTo( node, maxheight, delta.length(), door );
	}

EXPORT_FROM_DLL void PathNode::Disconnect
	(
	PathNode *node
	)

	{
	int i;

	for( i = 0; i < numChildren; i++ )
		{
		if ( Child[ i ].node == node->nodenum )
			{
			break;
			}
		}

	// Should never happen, but let it slide after release
	assert( i != numChildren );
	if ( i == numChildren )
		{
		return;
		}

	numChildren--;

	// Since we're not worried about the order of the nodes, just
	// move the last node into the slot occupied by the removed node.
	Child[ i ] = Child[ numChildren ];
	Child[ numChildren ].node = 0;
	Child[ numChildren ].moveCost = 0;
	}

EXPORT_FROM_DLL void PathNode::FindChildren
	(
	Event *ev
	)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -