mirror of
https://github.com/rehlds/rehlds.git
synced 2025-04-08 18:40:06 +03:00
535 lines
12 KiB
C++
535 lines
12 KiB
C++
/*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* In addition, as a special exception, the author gives permission to
|
|
* link the code of this program with the Half-Life Game Engine ("HL
|
|
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
|
|
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
|
* respects for all of the code used other than the HL Engine and MODs
|
|
* from Valve. If you modify this file, you may extend this exception
|
|
* to your version of the file, but you are not obligated to do so. If
|
|
* you do not wish to do so, delete this exception statement from your
|
|
* version.
|
|
*
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
// Returns false if any part of the bottom of the entity is off an edge that is not a staircase.
|
|
qboolean SV_CheckBottom(edict_t *ent)
|
|
{
|
|
vec3_t mins;
|
|
vec3_t maxs;
|
|
vec3_t start;
|
|
vec3_t stop;
|
|
trace_t trace;
|
|
int x;
|
|
int y;
|
|
float mid;
|
|
float bottom;
|
|
|
|
qboolean monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE;
|
|
|
|
VectorAdd(ent->v.origin, ent->v.mins, mins);
|
|
VectorAdd(ent->v.origin, ent->v.maxs, maxs);
|
|
|
|
// if all of the points under the corners are solid world, don't bother
|
|
// with the tougher checks
|
|
// the corners must be within 16 of the midpoint
|
|
start[2] = mins[2] - 1.0f;
|
|
|
|
for (x = 0; x <= 1; x++)
|
|
{
|
|
for (y = 0; y <= 1; y++)
|
|
{
|
|
start[0] = x ? maxs[0] : mins[0];
|
|
start[1] = y ? maxs[1] : mins[1];
|
|
|
|
g_groupmask = ent->v.groupinfo;
|
|
|
|
if (SV_PointContents(start) != CONTENTS_SOLID)
|
|
goto realcheck;
|
|
}
|
|
}
|
|
|
|
// we got out easy
|
|
return TRUE;
|
|
|
|
realcheck:
|
|
|
|
// check it for real...
|
|
start[2] = mins[2] + sv_stepsize.value;
|
|
|
|
// the midpoint must be within 16 of the bottom
|
|
start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5f;
|
|
start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5f;
|
|
|
|
stop[2] = start[2] - 2.0f * sv_stepsize.value;
|
|
trace = SV_Move(start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip);
|
|
|
|
if (trace.fraction == 1.0f)
|
|
return FALSE;
|
|
|
|
mid = bottom = trace.endpos[2];
|
|
|
|
// the corners must be within 16 of the midpoint
|
|
for (x = 0; x <= 1; x++)
|
|
{
|
|
for (y = 0; y <= 1; y++)
|
|
{
|
|
start[0] = stop[0] = x ? maxs[0] : mins[0];
|
|
start[1] = stop[1] = y ? maxs[1] : mins[1];
|
|
|
|
trace = SV_Move(start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, monsterClip);
|
|
|
|
if (trace.fraction != 1.0f && trace.endpos[2] > bottom)
|
|
bottom = trace.endpos[2];
|
|
|
|
if (trace.fraction == 1.0f || mid - trace.endpos[2] > sv_stepsize.value)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Called by monster program code.
|
|
// The move will be adjusted for slopes and stairs, but if the move isn't
|
|
// possible, no move is done, false is returned, and
|
|
// pr_global_struct->trace_normal is set to the normal of the blocking wall
|
|
qboolean SV_movetest(edict_t *ent, vec_t *move, qboolean relink)
|
|
{
|
|
vec3_t oldorg;
|
|
vec3_t neworg;
|
|
vec3_t end;
|
|
trace_t trace;
|
|
|
|
// try the move
|
|
VectorCopy(ent->v.origin, oldorg);
|
|
VectorAdd(ent->v.origin, move, neworg);
|
|
|
|
// push down from a step height above the wished position
|
|
neworg[2] += sv_stepsize.value;
|
|
VectorCopy(neworg, end);
|
|
end[2] -= sv_stepsize.value * 2.0f;
|
|
|
|
trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
|
|
|
|
if (trace.allsolid)
|
|
return FALSE;
|
|
|
|
if (trace.startsolid)
|
|
{
|
|
neworg[2] -= sv_stepsize.value;
|
|
trace = SV_MoveNoEnts(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
|
|
|
|
if (trace.allsolid || trace.startsolid)
|
|
return FALSE;
|
|
}
|
|
|
|
if (trace.fraction == 1.0f)
|
|
{
|
|
// if monster had the ground pulled out, go ahead and fall
|
|
if (ent->v.flags & FL_PARTIALGROUND)
|
|
{
|
|
VectorAdd(ent->v.origin, move, ent->v.origin);
|
|
|
|
if (relink)
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
}
|
|
|
|
// fall down
|
|
ent->v.flags &= ~FL_ONGROUND;
|
|
return TRUE;
|
|
}
|
|
|
|
// walked off an edge
|
|
return FALSE;
|
|
}
|
|
|
|
// check point traces down for dangling corners
|
|
VectorCopy(trace.endpos, ent->v.origin);
|
|
|
|
if (!SV_CheckBottom(ent))
|
|
{
|
|
if (!(ent->v.flags & FL_PARTIALGROUND))
|
|
{
|
|
VectorCopy(oldorg, ent->v.origin);
|
|
return FALSE;
|
|
}
|
|
|
|
// entity had floor mostly pulled out from underneath it
|
|
// and is trying to correct
|
|
}
|
|
else
|
|
{
|
|
if (ent->v.flags & FL_PARTIALGROUND)
|
|
{
|
|
// back on ground
|
|
ent->v.flags &= ~FL_PARTIALGROUND;
|
|
}
|
|
|
|
ent->v.groundentity = trace.ent;
|
|
}
|
|
|
|
// the move is ok
|
|
if (relink)
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Called by monster program code.
|
|
// The move will be adjusted for slopes and stairs, but if the move isn't
|
|
// possible, no move is done, false is returned, and
|
|
// pr_global_struct->trace_normal is set to the normal of the blocking wall
|
|
qboolean SV_movestep(edict_t *ent, vec_t *move, qboolean relink)
|
|
{
|
|
trace_t trace;
|
|
vec3_t neworg, oldorg, end;
|
|
|
|
// try the move
|
|
VectorCopy(ent->v.origin, oldorg);
|
|
VectorAdd(ent->v.origin, move, neworg);
|
|
|
|
qboolean monsterClipBrush = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE;
|
|
|
|
// flying monsters don't step up
|
|
if (ent->v.flags & (FL_FLY | FL_SWIM))
|
|
{
|
|
// try one move with vertical motion, then one without
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
VectorAdd(ent->v.origin, move, neworg);
|
|
|
|
edict_t *enemy = ent->v.enemy;
|
|
if (i == 0 && enemy)
|
|
{
|
|
float dz = ent->v.origin[2] - enemy->v.origin[2];
|
|
|
|
if (dz > 40.0f)
|
|
neworg[2] -= 8.0f;
|
|
else if (dz < 30.0f)
|
|
neworg[2] += 8.0f;
|
|
}
|
|
|
|
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, neworg, MOVE_NORMAL, ent, monsterClipBrush);
|
|
|
|
if (trace.fraction == 1.0f)
|
|
{
|
|
g_groupmask = ent->v.groupinfo;
|
|
|
|
if ((ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY)
|
|
{
|
|
// swim monster left water
|
|
return FALSE;
|
|
}
|
|
|
|
VectorCopy(trace.endpos, ent->v.origin);
|
|
|
|
if (relink)
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (!enemy)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// push down from a step height above the wished position
|
|
neworg[2] += sv_stepsize.value;
|
|
VectorCopy(neworg, end);
|
|
end[2] -= sv_stepsize.value * 2.0f;
|
|
|
|
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClipBrush);
|
|
|
|
if (trace.allsolid)
|
|
return FALSE;
|
|
|
|
if (trace.startsolid)
|
|
{
|
|
neworg[2] = neworg[2] - sv_stepsize.value;
|
|
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent, monsterClipBrush);
|
|
|
|
if (trace.allsolid || trace.startsolid)
|
|
return FALSE;
|
|
}
|
|
|
|
if (trace.fraction == 1.0f)
|
|
{
|
|
// if monster had the ground pulled out, go ahead and fall
|
|
if (ent->v.flags & FL_PARTIALGROUND)
|
|
{
|
|
VectorAdd(ent->v.origin, move, ent->v.origin);
|
|
|
|
if (relink)
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
}
|
|
|
|
// fall down
|
|
ent->v.flags &= ~FL_ONGROUND;
|
|
return TRUE;
|
|
}
|
|
|
|
// walked off an edge
|
|
return FALSE;
|
|
}
|
|
|
|
// check point traces down for dangling corners
|
|
VectorCopy(trace.endpos, ent->v.origin);
|
|
|
|
if (!SV_CheckBottom(ent))
|
|
{
|
|
if (!(ent->v.flags & FL_PARTIALGROUND))
|
|
{
|
|
VectorCopy(oldorg, ent->v.origin);
|
|
return FALSE;
|
|
}
|
|
|
|
// entity had floor mostly pulled out from underneath it
|
|
// and is trying to correct
|
|
}
|
|
else
|
|
{
|
|
if (ent->v.flags & FL_PARTIALGROUND)
|
|
{
|
|
// back on ground
|
|
ent->v.flags &= ~FL_PARTIALGROUND;
|
|
}
|
|
|
|
ent->v.groundentity = trace.ent;
|
|
}
|
|
|
|
// the move is ok
|
|
if (relink)
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Turns to the movement direction, and walks the current distance if facing it.
|
|
qboolean SV_StepDirection(edict_t *ent, float yaw, float dist)
|
|
{
|
|
vec3_t move;
|
|
|
|
yaw = yaw * (M_PI * 2.0f) / 360.0f;
|
|
|
|
move[0] = cos(yaw) * dist;
|
|
move[1] = sin(yaw) * dist;
|
|
move[2] = 0.0f;
|
|
|
|
if (SV_movestep(ent, move, FALSE))
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
SV_LinkEdict(ent, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
qboolean SV_FlyDirection(edict_t *ent, vec_t *direction)
|
|
{
|
|
if (SV_movestep(ent, direction, FALSE))
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
SV_LinkEdict(ent, TRUE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void SV_FixCheckBottom(edict_t *ent)
|
|
{
|
|
ent->v.flags |= FL_PARTIALGROUND;
|
|
}
|
|
|
|
const int DI_NODIR = -1;
|
|
|
|
NOXREF void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist)
|
|
{
|
|
NOXREFCHECK;
|
|
|
|
SV_NewChaseDir2(actor, enemy->v.origin, dist);
|
|
}
|
|
|
|
NOXREF qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist)
|
|
{
|
|
NOXREFCHECK;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (goal->v.absmin[i] > ent->v.absmax[i] + dist)
|
|
return FALSE;
|
|
|
|
if (goal->v.absmax[i] < ent->v.absmin[i] - dist)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NOXREF qboolean SV_ReachedGoal(edict_t *ent, vec_t *vecGoal, float flDist)
|
|
{
|
|
NOXREFCHECK;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (vecGoal[i] > ent->v.absmax[i] + flDist)
|
|
return FALSE;
|
|
|
|
if (vecGoal[i] < ent->v.absmin[i] - flDist)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void SV_NewChaseDir2(edict_t *actor, vec_t *vecGoal, float dist)
|
|
{
|
|
vec3_t d;
|
|
float deltax, deltay;
|
|
float tempdir, olddir, turnaround;
|
|
|
|
olddir = anglemod(45.0f * (int)(actor->v.ideal_yaw / 45.0f));
|
|
turnaround = anglemod(olddir - 180.0f);
|
|
|
|
deltax = vecGoal[0] - actor->v.origin[0];
|
|
deltay = vecGoal[1] - actor->v.origin[1];
|
|
|
|
if (deltax > 10.0f)
|
|
d[1] = 0.0f;
|
|
else if (deltax < -10.0f)
|
|
d[1] = 180.0f;
|
|
else
|
|
d[1] = DI_NODIR;
|
|
|
|
if (deltay < -10.0f)
|
|
d[2] = 270.0f;
|
|
else if (deltay > 10.0f)
|
|
d[2] = 90.0f;
|
|
else
|
|
d[2] = DI_NODIR;
|
|
|
|
// try direct route
|
|
if (d[1] != DI_NODIR && d[2] != DI_NODIR)
|
|
{
|
|
if (d[1] == 0.0f)
|
|
tempdir = d[2] == 90.0f ? 45.0f : 315.0f;
|
|
else
|
|
tempdir = d[2] == 90.0f ? 135.0f : 215.0f;
|
|
|
|
if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist))
|
|
return;
|
|
}
|
|
|
|
// try other directions
|
|
if (RandomLong(0, 1) || abs(deltay) > abs(deltax))
|
|
{
|
|
tempdir = d[1];
|
|
d[1] = d[2];
|
|
d[2] = tempdir;
|
|
}
|
|
|
|
if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist))
|
|
return;
|
|
|
|
if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist))
|
|
return;
|
|
|
|
// there is no direct path to the player, so pick another direction
|
|
if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist))
|
|
return;
|
|
|
|
// randomly determine direction of search
|
|
if (RandomLong(0, 1))
|
|
{
|
|
for (tempdir = 0.0f; tempdir <= 315.0f; tempdir += 45.0f)
|
|
{
|
|
if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist))
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (tempdir = 315.0f; tempdir >= 0.0f; tempdir -= 45.0f)
|
|
{
|
|
if (tempdir != turnaround && SV_StepDirection(actor, tempdir, dist))
|
|
return;
|
|
}
|
|
}
|
|
|
|
// we tried. run backwards. that ought to work...
|
|
if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist))
|
|
return;
|
|
|
|
// can't move, we're stuck somehow
|
|
actor->v.ideal_yaw = olddir;
|
|
|
|
// if a bridge was pulled out from underneath a monster, it may not have
|
|
// a valid standing position at all
|
|
if (!SV_CheckBottom(actor))
|
|
{
|
|
SV_FixCheckBottom(actor);
|
|
}
|
|
}
|
|
|
|
void EXT_FUNC SV_MoveToOrigin_I(edict_t *ent, const float *pflGoal, float dist, int iMoveType)
|
|
{
|
|
vec3_t vecGoal;
|
|
VectorCopy(pflGoal, vecGoal);
|
|
|
|
if (ent->v.flags & (FL_FLY | FL_SWIM | FL_ONGROUND))
|
|
{
|
|
if (iMoveType == MOVE_NORMAL)
|
|
{
|
|
if (!SV_StepDirection(ent, ent->v.ideal_yaw, dist))
|
|
{
|
|
SV_NewChaseDir2(ent, vecGoal, dist);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vec3_t vecDir;
|
|
VectorSubtract(vecGoal, ent->v.origin, vecDir);
|
|
|
|
if (!(ent->v.flags & (FL_SWIM | FL_FLY)))
|
|
vecDir[2] = 0.0f;
|
|
|
|
VectorNormalize(vecDir);
|
|
VectorScale(vecDir, dist, vecDir);
|
|
SV_FlyDirection(ent, vecDir);
|
|
}
|
|
}
|
|
}
|