Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

m_move.c (11476B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // m_move.c -- monster movement
     21 
     22 #include "g_local.h"
     23 
     24 #define	STEPSIZE	18
     25 
     26 /*
     27 =============
     28 M_CheckBottom
     29 
     30 Returns false if any part of the bottom of the entity is off an edge that
     31 is not a staircase.
     32 
     33 =============
     34 */
     35 int c_yes, c_no;
     36 
     37 qboolean M_CheckBottom (edict_t *ent)
     38 {
     39 	vec3_t	mins, maxs, start, stop;
     40 	trace_t	trace;
     41 	int		x, y;
     42 	float	mid, bottom;
     43 	
     44 	VectorAdd (ent->s.origin, ent->mins, mins);
     45 	VectorAdd (ent->s.origin, ent->maxs, maxs);
     46 
     47 // if all of the points under the corners are solid world, don't bother
     48 // with the tougher checks
     49 // the corners must be within 16 of the midpoint
     50 	start[2] = mins[2] - 1;
     51 	for	(x=0 ; x<=1 ; x++)
     52 		for	(y=0 ; y<=1 ; y++)
     53 		{
     54 			start[0] = x ? maxs[0] : mins[0];
     55 			start[1] = y ? maxs[1] : mins[1];
     56 			if (gi.pointcontents (start) != CONTENTS_SOLID)
     57 				goto realcheck;
     58 		}
     59 
     60 	c_yes++;
     61 	return true;		// we got out easy
     62 
     63 realcheck:
     64 	c_no++;
     65 //
     66 // check it for real...
     67 //
     68 	start[2] = mins[2];
     69 	
     70 // the midpoint must be within 16 of the bottom
     71 	start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
     72 	start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
     73 	stop[2] = start[2] - 2*STEPSIZE;
     74 	trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
     75 
     76 	if (trace.fraction == 1.0)
     77 		return false;
     78 	mid = bottom = trace.endpos[2];
     79 	
     80 // the corners must be within 16 of the midpoint	
     81 	for	(x=0 ; x<=1 ; x++)
     82 		for	(y=0 ; y<=1 ; y++)
     83 		{
     84 			start[0] = stop[0] = x ? maxs[0] : mins[0];
     85 			start[1] = stop[1] = y ? maxs[1] : mins[1];
     86 			
     87 			trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
     88 			
     89 			if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
     90 				bottom = trace.endpos[2];
     91 			if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
     92 				return false;
     93 		}
     94 
     95 	c_yes++;
     96 	return true;
     97 }
     98 
     99 
    100 /*
    101 =============
    102 SV_movestep
    103 
    104 Called by monster program code.
    105 The move will be adjusted for slopes and stairs, but if the move isn't
    106 possible, no move is done, false is returned, and
    107 pr_global_struct->trace_normal is set to the normal of the blocking wall
    108 =============
    109 */
    110 //FIXME since we need to test end position contents here, can we avoid doing
    111 //it again later in catagorize position?
    112 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
    113 {
    114 	float		dz;
    115 	vec3_t		oldorg, neworg, end;
    116 	trace_t		trace;
    117 	int			i;
    118 	float		stepsize;
    119 	vec3_t		test;
    120 	int			contents;
    121 
    122 // try the move	
    123 	VectorCopy (ent->s.origin, oldorg);
    124 	VectorAdd (ent->s.origin, move, neworg);
    125 
    126 // flying monsters don't step up
    127 	if ( ent->flags & (FL_SWIM | FL_FLY) )
    128 	{
    129 	// try one move with vertical motion, then one without
    130 		for (i=0 ; i<2 ; i++)
    131 		{
    132 			VectorAdd (ent->s.origin, move, neworg);
    133 			if (i == 0 && ent->enemy)
    134 			{
    135 				if (!ent->goalentity)
    136 					ent->goalentity = ent->enemy;
    137 				dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
    138 				if (ent->goalentity->client)
    139 				{
    140 					if (dz > 40)
    141 						neworg[2] -= 8;
    142 					if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
    143 						if (dz < 30)
    144 							neworg[2] += 8;
    145 				}
    146 				else
    147 				{
    148 					if (dz > 8)
    149 						neworg[2] -= 8;
    150 					else if (dz > 0)
    151 						neworg[2] -= dz;
    152 					else if (dz < -8)
    153 						neworg[2] += 8;
    154 					else
    155 						neworg[2] += dz;
    156 				}
    157 			}
    158 			trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
    159 	
    160 			// fly monsters don't enter water voluntarily
    161 			if (ent->flags & FL_FLY)
    162 			{
    163 				if (!ent->waterlevel)
    164 				{
    165 					test[0] = trace.endpos[0];
    166 					test[1] = trace.endpos[1];
    167 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
    168 					contents = gi.pointcontents(test);
    169 					if (contents & MASK_WATER)
    170 						return false;
    171 				}
    172 			}
    173 
    174 			// swim monsters don't exit water voluntarily
    175 			if (ent->flags & FL_SWIM)
    176 			{
    177 				if (ent->waterlevel < 2)
    178 				{
    179 					test[0] = trace.endpos[0];
    180 					test[1] = trace.endpos[1];
    181 					test[2] = trace.endpos[2] + ent->mins[2] + 1;
    182 					contents = gi.pointcontents(test);
    183 					if (!(contents & MASK_WATER))
    184 						return false;
    185 				}
    186 			}
    187 
    188 			if (trace.fraction == 1)
    189 			{
    190 				VectorCopy (trace.endpos, ent->s.origin);
    191 				if (relink)
    192 				{
    193 					gi.linkentity (ent);
    194 					G_TouchTriggers (ent);
    195 				}
    196 				return true;
    197 			}
    198 			
    199 			if (!ent->enemy)
    200 				break;
    201 		}
    202 		
    203 		return false;
    204 	}
    205 
    206 // push down from a step height above the wished position
    207 	if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
    208 		stepsize = STEPSIZE;
    209 	else
    210 		stepsize = 1;
    211 
    212 	neworg[2] += stepsize;
    213 	VectorCopy (neworg, end);
    214 	end[2] -= stepsize*2;
    215 
    216 	trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
    217 
    218 	if (trace.allsolid)
    219 		return false;
    220 
    221 	if (trace.startsolid)
    222 	{
    223 		neworg[2] -= stepsize;
    224 		trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
    225 		if (trace.allsolid || trace.startsolid)
    226 			return false;
    227 	}
    228 
    229 
    230 	// don't go in to water
    231 	if (ent->waterlevel == 0)
    232 	{
    233 		test[0] = trace.endpos[0];
    234 		test[1] = trace.endpos[1];
    235 		test[2] = trace.endpos[2] + ent->mins[2] + 1;	
    236 		contents = gi.pointcontents(test);
    237 
    238 		if (contents & MASK_WATER)
    239 			return false;
    240 	}
    241 
    242 	if (trace.fraction == 1)
    243 	{
    244 	// if monster had the ground pulled out, go ahead and fall
    245 		if ( ent->flags & FL_PARTIALGROUND )
    246 		{
    247 			VectorAdd (ent->s.origin, move, ent->s.origin);
    248 			if (relink)
    249 			{
    250 				gi.linkentity (ent);
    251 				G_TouchTriggers (ent);
    252 			}
    253 			ent->groundentity = NULL;
    254 			return true;
    255 		}
    256 	
    257 		return false;		// walked off an edge
    258 	}
    259 
    260 // check point traces down for dangling corners
    261 	VectorCopy (trace.endpos, ent->s.origin);
    262 	
    263 	if (!M_CheckBottom (ent))
    264 	{
    265 		if ( ent->flags & FL_PARTIALGROUND )
    266 		{	// entity had floor mostly pulled out from underneath it
    267 			// and is trying to correct
    268 			if (relink)
    269 			{
    270 				gi.linkentity (ent);
    271 				G_TouchTriggers (ent);
    272 			}
    273 			return true;
    274 		}
    275 		VectorCopy (oldorg, ent->s.origin);
    276 		return false;
    277 	}
    278 
    279 	if ( ent->flags & FL_PARTIALGROUND )
    280 	{
    281 		ent->flags &= ~FL_PARTIALGROUND;
    282 	}
    283 	ent->groundentity = trace.ent;
    284 	ent->groundentity_linkcount = trace.ent->linkcount;
    285 
    286 // the move is ok
    287 	if (relink)
    288 	{
    289 		gi.linkentity (ent);
    290 		G_TouchTriggers (ent);
    291 	}
    292 	return true;
    293 }
    294 
    295 
    296 //============================================================================
    297 
    298 /*
    299 ===============
    300 M_ChangeYaw
    301 
    302 ===============
    303 */
    304 void M_ChangeYaw (edict_t *ent)
    305 {
    306 	float	ideal;
    307 	float	current;
    308 	float	move;
    309 	float	speed;
    310 	
    311 	current = anglemod(ent->s.angles[YAW]);
    312 	ideal = ent->ideal_yaw;
    313 
    314 	if (current == ideal)
    315 		return;
    316 
    317 	move = ideal - current;
    318 	speed = ent->yaw_speed;
    319 	if (ideal > current)
    320 	{
    321 		if (move >= 180)
    322 			move = move - 360;
    323 	}
    324 	else
    325 	{
    326 		if (move <= -180)
    327 			move = move + 360;
    328 	}
    329 	if (move > 0)
    330 	{
    331 		if (move > speed)
    332 			move = speed;
    333 	}
    334 	else
    335 	{
    336 		if (move < -speed)
    337 			move = -speed;
    338 	}
    339 	
    340 	ent->s.angles[YAW] = anglemod (current + move);
    341 }
    342 
    343 
    344 /*
    345 ======================
    346 SV_StepDirection
    347 
    348 Turns to the movement direction, and walks the current distance if
    349 facing it.
    350 
    351 ======================
    352 */
    353 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
    354 {
    355 	vec3_t		move, oldorigin;
    356 	float		delta;
    357 	
    358 	ent->ideal_yaw = yaw;
    359 	M_ChangeYaw (ent);
    360 	
    361 	yaw = yaw*M_PI*2 / 360;
    362 	move[0] = cos(yaw)*dist;
    363 	move[1] = sin(yaw)*dist;
    364 	move[2] = 0;
    365 
    366 	VectorCopy (ent->s.origin, oldorigin);
    367 	if (SV_movestep (ent, move, false))
    368 	{
    369 		delta = ent->s.angles[YAW] - ent->ideal_yaw;
    370 		if (delta > 45 && delta < 315)
    371 		{		// not turned far enough, so don't take the step
    372 			VectorCopy (oldorigin, ent->s.origin);
    373 		}
    374 		gi.linkentity (ent);
    375 		G_TouchTriggers (ent);
    376 		return true;
    377 	}
    378 	gi.linkentity (ent);
    379 	G_TouchTriggers (ent);
    380 	return false;
    381 }
    382 
    383 /*
    384 ======================
    385 SV_FixCheckBottom
    386 
    387 ======================
    388 */
    389 void SV_FixCheckBottom (edict_t *ent)
    390 {
    391 	ent->flags |= FL_PARTIALGROUND;
    392 }
    393 
    394 
    395 
    396 /*
    397 ================
    398 SV_NewChaseDir
    399 
    400 ================
    401 */
    402 #define	DI_NODIR	-1
    403 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
    404 {
    405 	float	deltax,deltay;
    406 	float	d[3];
    407 	float	tdir, olddir, turnaround;
    408 
    409 	//FIXME: how did we get here with no enemy
    410 	if (!enemy)
    411 		return;
    412 
    413 	olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
    414 	turnaround = anglemod(olddir - 180);
    415 
    416 	deltax = enemy->s.origin[0] - actor->s.origin[0];
    417 	deltay = enemy->s.origin[1] - actor->s.origin[1];
    418 	if (deltax>10)
    419 		d[1]= 0;
    420 	else if (deltax<-10)
    421 		d[1]= 180;
    422 	else
    423 		d[1]= DI_NODIR;
    424 	if (deltay<-10)
    425 		d[2]= 270;
    426 	else if (deltay>10)
    427 		d[2]= 90;
    428 	else
    429 		d[2]= DI_NODIR;
    430 
    431 // try direct route
    432 	if (d[1] != DI_NODIR && d[2] != DI_NODIR)
    433 	{
    434 		if (d[1] == 0)
    435 			tdir = d[2] == 90 ? 45 : 315;
    436 		else
    437 			tdir = d[2] == 90 ? 135 : 215;
    438 			
    439 		if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
    440 			return;
    441 	}
    442 
    443 // try other directions
    444 	if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
    445 	{
    446 		tdir=d[1];
    447 		d[1]=d[2];
    448 		d[2]=tdir;
    449 	}
    450 
    451 	if (d[1]!=DI_NODIR && d[1]!=turnaround 
    452 	&& SV_StepDirection(actor, d[1], dist))
    453 			return;
    454 
    455 	if (d[2]!=DI_NODIR && d[2]!=turnaround
    456 	&& SV_StepDirection(actor, d[2], dist))
    457 			return;
    458 
    459 /* there is no direct path to the player, so pick another direction */
    460 
    461 	if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
    462 			return;
    463 
    464 	if (rand()&1) 	/*randomly determine direction of search*/
    465 	{
    466 		for (tdir=0 ; tdir<=315 ; tdir += 45)
    467 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
    468 					return;
    469 	}
    470 	else
    471 	{
    472 		for (tdir=315 ; tdir >=0 ; tdir -= 45)
    473 			if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
    474 					return;
    475 	}
    476 
    477 	if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
    478 			return;
    479 
    480 	actor->ideal_yaw = olddir;		// can't move
    481 
    482 // if a bridge was pulled out from underneath a monster, it may not have
    483 // a valid standing position at all
    484 
    485 	if (!M_CheckBottom (actor))
    486 		SV_FixCheckBottom (actor);
    487 }
    488 
    489 /*
    490 ======================
    491 SV_CloseEnough
    492 
    493 ======================
    494 */
    495 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
    496 {
    497 	int		i;
    498 	
    499 	for (i=0 ; i<3 ; i++)
    500 	{
    501 		if (goal->absmin[i] > ent->absmax[i] + dist)
    502 			return false;
    503 		if (goal->absmax[i] < ent->absmin[i] - dist)
    504 			return false;
    505 	}
    506 	return true;
    507 }
    508 
    509 
    510 /*
    511 ======================
    512 M_MoveToGoal
    513 ======================
    514 */
    515 void M_MoveToGoal (edict_t *ent, float dist)
    516 {
    517 	edict_t		*goal;
    518 	
    519 	goal = ent->goalentity;
    520 
    521 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
    522 		return;
    523 
    524 // if the next step hits the enemy, return immediately
    525 	if (ent->enemy &&  SV_CloseEnough (ent, ent->enemy, dist) )
    526 		return;
    527 
    528 // bump around...
    529 	if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
    530 	{
    531 		if (ent->inuse)
    532 			SV_NewChaseDir (ent, goal, dist);
    533 	}
    534 }
    535 
    536 
    537 /*
    538 ===============
    539 M_walkmove
    540 ===============
    541 */
    542 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
    543 {
    544 	vec3_t	move;
    545 	
    546 	if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
    547 		return false;
    548 
    549 	yaw = yaw*M_PI*2 / 360;
    550 	
    551 	move[0] = cos(yaw)*dist;
    552 	move[1] = sin(yaw)*dist;
    553 	move[2] = 0;
    554 
    555 	return SV_movestep(ent, move, true);
    556 }