q2wf-portable/bot_nav.c

452 lines
11 KiB
C
Raw Permalink Normal View History

/*****************************************************************
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
..............................................................
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
..............................................................
All other files are Copyright(c) Id Software, Inc.
Please see liscense.txt in the source directory for the copyright
information regarding those files belonging to Id Software, Inc.
..............................................................
Should you decide to release a modified version of Eraser, you MUST
include the following text (minus the BEGIN and END lines) in the
documentation for your modification.
--- BEGIN ---
The Eraser Bot is a product of Ryan Feltrin, and is available from
the Eraser Bot homepage, at http://impact.frag.com.
This program is a modification of the Eraser Bot, and is therefore
in NO WAY supported by Ryan Feltrin.
This program MUST NOT be sold in ANY form. If you have paid for
this product, you should contact Ryan Feltrin immediately, via
the Eraser Bot homepage.
--- END ---
..............................................................
You will find p_trail.c has not been included with the Eraser
source code release. This is NOT an error. I am unable to
distribute this file because it contains code that is bound by
legal documents, and signed by myself, never to be released
to the public. Sorry guys, but law is law.
I have therefore include the compiled version of these files
in .obj form in the src\Release and src\Debug directories.
So while you cannot edit and debug code within these files,
you can still compile this source as-is. Although these will only
work in MSVC v5.0, linux versions can be made available upon
request.
NOTE: When compiling this source, you will get a warning
message from the compiler, regarding the missing p_trail.c
file. Just ignore it, it will still compile fine.
..............................................................
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
use of this source code. I also am NOT willing to provide any form
of help or support for this source code. It is provided as-is,
as a service by me, with no documentation, other then the comments
contained within the code. If you have any queries, I suggest you
visit the "official" Eraser source web-board, at
http://www.telefragged.com/epidemic/. I will stop by there from
time to time, to answer questions and help with any problems that
may arise.
Otherwise, have fun, and I look forward to seeing what can be done
with this.
-Ryan Feltrin
-Acrid-
*****************************************************************/
//
// bot_nav.c
//
// This file conatins mostly (simple) environment sampling, which
// in no way is related to the the navigation code, contained within
// p_trail.c
//
#include "g_local.h"
#include "m_player.h"
#include "bot_procs.h"
#include "p_trail.h"
/*
================
botRoamFindBestDirection
Finds the best direction to walk for the next few seconds
Set ideal_yaw accordingly
================
*/
#define TRACE_DIST 256
void botRoamFindBestDirection(edict_t *self)
{
float best_dist=0, this_dist, best_yaw;
int i;
vec3_t dir, dest, angle, this_angle;
vec3_t mins;
trace_t trace;
if (self->last_best_direction > (level.time - 1))
return;
self->last_best_direction = level.time;
bestdirection_callsthisframe++;
if (bestdirection_callsthisframe > 2)
return;
best_yaw = self->ideal_yaw;
VectorAdd(self->mins, tv(0,0,STEPSIZE), mins);
// check eight compass directions
VectorClear(angle);
VectorClear(this_angle);
angle[1] = self->ideal_yaw;
// start at center, then fan out in 45 degree intervals, swapping between + and -
for (i=1; i<8; i++)
{
// if (i<7 && random() < 0.4) // skip random intervals
// i++;
if (i==4)
i=6;
this_angle[1] = anglemod(angle[1] + ((((i % 2)*2 - 1) * (int) (floor(i/2))) * 45));
AngleVectors(this_angle, dir, NULL, NULL);
VectorMA(self->s.origin, TRACE_DIST, dir, dest);
trace = gi.trace(self->s.origin, mins, self->maxs, dest, self, MASK_SOLID);
if (trace.fraction > 0)
{ // check that destination is onground, or not above lava/slime
dest[0] = trace.endpos[0];
dest[1] = trace.endpos[1];
dest[2] = trace.endpos[2] - 32;
if (gi.pointcontents(dest) & MASK_PLAYERSOLID)
goto nocheckground;
this_dist = trace.fraction * TRACE_DIST;
VectorCopy(trace.endpos, dest);
dest[2] -= 256;
trace = gi.trace(trace.endpos, VEC_ORIGIN, VEC_ORIGIN, dest, self, MASK_SOLID | MASK_WATER);
if ( ! ((trace.fraction == 1) || (trace.contents & MASK_WATER))) // avoid ALL forms of liquid for now
{
//gi.dprintf("Dist %i: %i\n", (int) this_angle[1], (int) this_dist);
if (trace.fraction > 0.4) // if there is a drop in this direction, try to avoid it if possible
this_dist *= 0.5;
nocheckground:
if (this_dist > best_dist)
{
best_dist = this_dist;
best_yaw = this_angle[1];
if (this_dist == TRACE_DIST)
break;
}
}
}
}
//gi.dprintf("Best yaw: %i\n", (int) best_yaw);
self->ideal_yaw = best_yaw;
}
void botRandomJump(edict_t *self)
{
vec3_t dir, right, angles;
if (self->groundentity)
{
if (self->last_jump > (level.time - 0.5))
return;
}
else
{
// if (self->last_jump > (level.time - 2))
return;
}
if (!CanJump(self))
return;
botRoamFindBestDirection(self);
VectorClear(angles);
angles[1] = self->ideal_yaw;
AngleVectors(angles, dir, right, NULL);
// VectorScale(dir, (crandom() + 0.5) / 1.5, dir);
// VectorScale(right, crandom() * 0.5, right);
// VectorAdd(dir, right, dir);
VectorNormalize2(dir, dir);
VectorScale(dir, 300 * ((random() * 0.6) + 0.4), dir);
VectorCopy(dir, self->velocity);
self->velocity[2] = 300;
self->groundentity = NULL;
// self->s.origin[2] += 1;
gi.linkentity(self);
VectorCopy(self->velocity, self->jump_velocity);
if (self->groundentity)
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
self->last_jump = level.time;
}
extern edict_t *pm_passent;
extern trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
qboolean touched_player;
void BotMoveThink (edict_t *ent, usercmd_t *ucmd)
{
gclient_t *client;
edict_t *other;
int i, j;
pmove_t pm;
level.current_entity = ent;
client = ent->client;
if (level.intermissiontime)
{
client->ps.pmove.pm_type = PM_FREEZE;
// can exit intermission after five seconds
if (level.time > level.intermissiontime + 5.0
&& (ucmd->buttons & BUTTON_ANY) )
level.exitintermission = true;
return;
}
pm_passent = ent;
// set up for pmove
memset (&pm, 0, sizeof(pm));
if (ent->movetype == MOVETYPE_NOCLIP)
client->ps.pmove.pm_type = PM_SPECTATOR;
else if (ent->s.modelindex != 255)
client->ps.pmove.pm_type = PM_GIB;
else if (ent->deadflag)
client->ps.pmove.pm_type = PM_DEAD;
else if (level.time < ent->frozentime)//botfreeze 3/99 needed still?
client->ps.pmove.pm_type = PM_DEAD;//3/99
else
client->ps.pmove.pm_type = PM_NORMAL;
client->ps.pmove.gravity = sv_gravity->value;
if (ent->maxs[2] == 4)
{
client->ps.pmove.pm_flags |= PMF_DUCKED;
ucmd->upmove = -400;
}
pm.s = client->ps.pmove;
for (i=0 ; i<3 ; i++)
{
pm.s.origin[i] = ent->s.origin[i]*8;
pm.s.velocity[i] = ent->velocity[i]*8;
}
/*
// if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
// {
pm.snapinitial = true;
// gi.dprintf ("pmove changed!\n");
// }
*/
// ucmd->buttons = 128;
pm.cmd = *ucmd;
pm.trace = PM_trace; // adds default parms
pm.pointcontents = gi.pointcontents;
// perform a pmove
gi.Pmove (&pm);
// save results of pmove
client->ps.pmove = pm.s;
client->old_pmove = pm.s;
for (i=0 ; i<3 ; i++)
{
ent->s.origin[i] = pm.s.origin[i]*0.125;
ent->velocity[i] = pm.s.velocity[i]*0.125;
}
VectorCopy (pm.mins, ent->mins);
VectorCopy (pm.maxs, ent->maxs);
client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
ent->viewheight = pm.viewheight;
ent->waterlevel = pm.waterlevel;
ent->watertype = pm.watertype;
ent->groundentity = pm.groundentity;
if (pm.groundentity)
ent->groundentity_linkcount = pm.groundentity->linkcount;
if (ent->deadflag)
{
client->ps.viewangles[ROLL] = 40;
client->ps.viewangles[PITCH] = -15;
client->ps.viewangles[YAW] = client->killer_yaw;
}
else
{
VectorCopy (pm.viewangles, client->v_angle);
VectorCopy (pm.viewangles, client->ps.viewangles);
}
gi.linkentity (ent);
if (ent->movetype != MOVETYPE_NOCLIP)
G_TouchTriggers (ent);
touched_player = false;
// touch other objects
for (i=0 ; i<pm.numtouch ; i++)
{
other = pm.touchents[i];
for (j=0 ; j<i ; j++)
if (pm.touchents[j] == other)
break;
if (j != i)
continue; // duplicated
if (other->client)
touched_player = true;
if (!other->touch)
continue;
other->touch (other, ent, NULL, NULL);
}
}
int botJumpAvoidEnt(edict_t *self, edict_t *e_avoid)
{
vec3_t dir, trail_vec, vec, tr_end;
float avoid_dist;
trace_t tr;
if (!CanJump(self))
return false;
if (e_avoid->owner && (((int) dmflags->value) & DF_NO_FRIENDLY_FIRE) && SameTeam(self, e_avoid->owner))
return 2;
//fixme acrid
if ((avoid_dist = entdist(self, e_avoid)) > 300)
{
self->avoid_ent = NULL;
return 2; // just keep going for our current goal
}
if (self->movetarget && (entdist(self, self->movetarget) < 256))
{
return 2;
}
if (!visible(self, e_avoid))
return 2;
// make sure we attack this person, if not currently attacking anything
if (!self->enemy && e_avoid->owner && e_avoid->owner->client)
self->enemy = e_avoid->owner;
VectorSubtract(self->s.origin, e_avoid->s.origin, dir);
VectorNormalize2(dir, dir);
trail_vec[0] = dir[1];
trail_vec[1] = dir[0];
trail_vec[2] = 0;
// determine which side we're on, so we know which way to try and dodge
VectorMA(e_avoid->s.origin, 4, trail_vec, vec);
VectorSubtract(self->s.origin, vec, vec);
if (avoid_dist < 200)
{
if (VectorLength(vec) > avoid_dist)
{
VectorScale(trail_vec, -1, trail_vec);
}
}
else if (random() < 0.5) // pick a random direction
{
VectorScale(trail_vec, -1, trail_vec);
}
// add some forwards/backwards movement
VectorMA(trail_vec, crandom(), dir, trail_vec);
VectorNormalize2(trail_vec, trail_vec);
if (self->groundentity)
{
VectorMA(e_avoid->s.origin, 200, trail_vec, vec);
tr = gi.trace(self->s.origin, vec3_origin, vec3_origin, vec, self, MASK_PLAYERSOLID);
VectorCopy(tr.endpos, tr_end);
tr_end[2] -= 512;
tr = gi.trace(tr.endpos, vec3_origin, vec3_origin, tr_end, self, MASK_PLAYERSOLID | MASK_WATER);
if (!(tr.contents & (CONTENTS_LAVA | CONTENTS_SLIME)))/* && ((e_avoid->s.effects & EF_GRENADE) || (avoid_dist < 160)))*/
{ // go ahead and jump!
VectorScale(trail_vec, BOT_RUN_SPEED, dir);
dir[2] = 300;
VectorCopy(dir, self->velocity);
VectorCopy(dir, self->jump_velocity);
self->groundentity = NULL;
// self->s.origin[2] += 1;
gi.linkentity(self);
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
}
else // strafe away
{
VectorCopy(trail_vec, self->avoid_dir);
self->avoid_dir_time = level.time + 0.3;
}
}
else if (self->waterlevel)
{
VectorCopy(trail_vec, self->avoid_dir);
self->avoid_dir_time = level.time + 1;
}
return 1;
}