mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
696 lines
19 KiB
C
696 lines
19 KiB
C
|
/*
|
||
|
Copyright (C) 1998 Steve Yeager
|
||
|
|
||
|
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.
|
||
|
|
||
|
*/
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// ACE - Quake II Bot Base Code
|
||
|
//
|
||
|
// Version 1.0
|
||
|
//
|
||
|
// This file is Copyright(c), Steve Yeager 1998, 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 ACE, you MUST
|
||
|
// include the following text (minus the BEGIN and END lines) in the
|
||
|
// documentation for your modification.
|
||
|
//
|
||
|
// --- BEGIN ---
|
||
|
//
|
||
|
// The ACE Bot is a product of Steve Yeager, and is available from
|
||
|
// the ACE Bot homepage, at http://www.axionfx.com/ace.
|
||
|
//
|
||
|
// This program is a modification of the ACE Bot, and is therefore
|
||
|
// in NO WAY supported by Steve Yeager.
|
||
|
//
|
||
|
// --- END ---
|
||
|
//
|
||
|
// I, Steve Yeager, hold no responsibility for any harm caused by the
|
||
|
// use of this source code, especially to small children and animals.
|
||
|
// It is provided as-is with no implied warranty or support.
|
||
|
//
|
||
|
// I also wish to thank and acknowledge the great work of others
|
||
|
// that has helped me to develop this code.
|
||
|
//
|
||
|
// John Cricket - For ideas and swapping code.
|
||
|
// Ryan Feltrin - For ideas and swapping code.
|
||
|
// SABIN - For showing how to do true client based movement.
|
||
|
// BotEpidemic - For keeping us up to date.
|
||
|
// Telefragged.com - For giving ACE a home.
|
||
|
// Microsoft - For giving us such a wonderful crash free OS.
|
||
|
// id - Need I say more.
|
||
|
//
|
||
|
// And to all the other testers, pathers, and players and people
|
||
|
// who I can't remember who the heck they were, but helped out.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// acebot_movement.c - This file contains all of the
|
||
|
// movement routines for the ACE bot
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "g_local.h"
|
||
|
#include "acebot.h"
|
||
|
|
||
|
// Platform states
|
||
|
#define STATE_TOP 0
|
||
|
#define STATE_BOTTOM 1
|
||
|
#define STATE_UP 2
|
||
|
#define STATE_DOWN 3
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Checks if bot can move (really just checking the ground)
|
||
|
// Also, this is not a real accurate check, but does a
|
||
|
// pretty good job and looks for lava/slime.
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
qboolean ACEMV_CanMove(edict_t *self, int direction)
|
||
|
{
|
||
|
vec3_t forward, right;
|
||
|
vec3_t offset,start,end;
|
||
|
vec3_t angles;
|
||
|
trace_t tr;
|
||
|
|
||
|
// Now check to see if move will move us off an edge
|
||
|
VectorCopy(self->s.angles,angles);
|
||
|
|
||
|
if(direction == MOVE_LEFT)
|
||
|
angles[1] += 90;
|
||
|
else if(direction == MOVE_RIGHT)
|
||
|
angles[1] -= 90;
|
||
|
else if(direction == MOVE_BACK)
|
||
|
angles[1] -=180;
|
||
|
|
||
|
// Set up the vectors
|
||
|
AngleVectors (angles, forward, right, NULL);
|
||
|
|
||
|
VectorSet(offset, 36, 0, 24);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
||
|
|
||
|
VectorSet(offset, 36, 0, -400);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, end);
|
||
|
|
||
|
tr = gi.trace(start, NULL, NULL, end, self, MASK_OPAQUE);
|
||
|
|
||
|
if(tr.fraction > 0.3 && tr.fraction != 1 || tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME))
|
||
|
{
|
||
|
if(debug_mode)
|
||
|
debug_printf("%s: move blocked\n",self->client->pers.netname);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true; // yup, can move
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Handle special cases of crouch/jump
|
||
|
//
|
||
|
// If the move is resolved here, this function returns
|
||
|
// true.
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
qboolean ACEMV_SpecialMove(edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
vec3_t dir,forward,right,start,end,offset;
|
||
|
vec3_t top;
|
||
|
trace_t tr;
|
||
|
|
||
|
// Get current direction
|
||
|
VectorCopy(self->client->ps.viewangles,dir);
|
||
|
dir[YAW] = self->s.angles[YAW];
|
||
|
AngleVectors (dir, forward, right, NULL);
|
||
|
|
||
|
VectorSet(offset, 18, 0, 0);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, start);
|
||
|
offset[0] += 18;
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, end);
|
||
|
|
||
|
// trace it
|
||
|
start[2] += 18; // so they are not jumping all the time
|
||
|
end[2] += 18;
|
||
|
tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
|
||
|
|
||
|
if(tr.allsolid)
|
||
|
{
|
||
|
// Check for crouching
|
||
|
start[2] -= 14;
|
||
|
end[2] -= 14;
|
||
|
|
||
|
// Set up for crouching check
|
||
|
VectorCopy(self->maxs,top);
|
||
|
top[2] = 0.0; // crouching height
|
||
|
tr = gi.trace (start, self->mins, top, end, self, MASK_PLAYERSOLID);
|
||
|
|
||
|
// Crouch
|
||
|
if(!tr.allsolid)
|
||
|
{
|
||
|
ucmd->forwardmove = 400;
|
||
|
ucmd->upmove = -400;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check for jump
|
||
|
start[2] += 32;
|
||
|
end[2] += 32;
|
||
|
tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
|
||
|
|
||
|
if(!tr.allsolid)
|
||
|
{
|
||
|
ucmd->forwardmove = 400;
|
||
|
ucmd->upmove = 400;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false; // We did not resolve a move here
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Checks for obstructions in front of bot
|
||
|
//
|
||
|
// This is a function I created origianlly for ACE that
|
||
|
// tries to help steer the bot around obstructions.
|
||
|
//
|
||
|
// If the move is resolved here, this function returns true.
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
qboolean ACEMV_CheckEyes(edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
vec3_t forward, right;
|
||
|
vec3_t leftstart, rightstart,focalpoint;
|
||
|
vec3_t upstart,upend;
|
||
|
vec3_t dir,offset;
|
||
|
|
||
|
trace_t traceRight,traceLeft,traceUp, traceFront; // for eyesight
|
||
|
|
||
|
// Get current angle and set up "eyes"
|
||
|
VectorCopy(self->s.angles,dir);
|
||
|
AngleVectors (dir, forward, right, NULL);
|
||
|
|
||
|
// Let them move to targets by walls
|
||
|
if(!self->movetarget)
|
||
|
VectorSet(offset,200,0,4); // focalpoint
|
||
|
else
|
||
|
VectorSet(offset,36,0,4); // focalpoint
|
||
|
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, focalpoint);
|
||
|
|
||
|
// Check from self to focalpoint
|
||
|
// Ladder code
|
||
|
VectorSet(offset,36,0,0); // set as high as possible
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, upend);
|
||
|
traceFront = gi.trace(self->s.origin, self->mins, self->maxs, upend, self, MASK_OPAQUE);
|
||
|
|
||
|
if(traceFront.contents & 0x8000000) // using detail brush here cuz sometimes it does not pick up ladders...??
|
||
|
{
|
||
|
ucmd->upmove = 400;
|
||
|
ucmd->forwardmove = 400;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// If this check fails we need to continue on with more detailed checks
|
||
|
if(traceFront.fraction == 1)
|
||
|
{
|
||
|
ucmd->forwardmove = 400;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
VectorSet(offset, 0, 18, 4);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, leftstart);
|
||
|
|
||
|
offset[1] -= 36; // want to make sure this is correct
|
||
|
//VectorSet(offset, 0, -18, 4);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, rightstart);
|
||
|
|
||
|
traceRight = gi.trace(rightstart, NULL, NULL, focalpoint, self, MASK_OPAQUE);
|
||
|
traceLeft = gi.trace(leftstart, NULL, NULL, focalpoint, self, MASK_OPAQUE);
|
||
|
|
||
|
// Wall checking code, this will degenerate progressivly so the least cost
|
||
|
// check will be done first.
|
||
|
|
||
|
// If open space move ok
|
||
|
if(traceRight.fraction != 1 || traceLeft.fraction != 1 || strcmp(traceLeft.ent->classname,"func_door")!=0)
|
||
|
{
|
||
|
// Special uppoint logic to check for slopes/stairs/jumping etc.
|
||
|
VectorSet(offset, 0, 18, 24);
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, upstart);
|
||
|
|
||
|
VectorSet(offset,0,0,200); // scan for height above head
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, upend);
|
||
|
traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE);
|
||
|
|
||
|
VectorSet(offset,200,0,200*traceUp.fraction-5); // set as high as possible
|
||
|
G_ProjectSource (self->s.origin, offset, forward, right, upend);
|
||
|
traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE);
|
||
|
|
||
|
// If the upper trace is not open, we need to turn.
|
||
|
if(traceUp.fraction != 1)
|
||
|
{
|
||
|
if(traceRight.fraction > traceLeft.fraction)
|
||
|
self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0;
|
||
|
else
|
||
|
self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0;
|
||
|
|
||
|
ucmd->forwardmove = 400;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Make the change in angles a little more gradual, not so snappy
|
||
|
// Subtle, but noticeable.
|
||
|
//
|
||
|
// Modified from the original id ChangeYaw code...
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
void ACEMV_ChangeBotAngle (edict_t *ent)
|
||
|
{
|
||
|
float ideal_yaw;
|
||
|
float ideal_pitch;
|
||
|
float current_yaw;
|
||
|
float current_pitch;
|
||
|
float move;
|
||
|
float speed;
|
||
|
vec3_t ideal_angle;
|
||
|
|
||
|
// Normalize the move angle first
|
||
|
VectorNormalize(ent->move_vector);
|
||
|
|
||
|
current_yaw = anglemod(ent->s.angles[YAW]);
|
||
|
current_pitch = anglemod(ent->s.angles[PITCH]);
|
||
|
|
||
|
vectoangles (ent->move_vector, ideal_angle);
|
||
|
|
||
|
ideal_yaw = anglemod(ideal_angle[YAW]);
|
||
|
ideal_pitch = anglemod(ideal_angle[PITCH]);
|
||
|
|
||
|
// Yaw
|
||
|
if (current_yaw != ideal_yaw)
|
||
|
{
|
||
|
move = ideal_yaw - current_yaw;
|
||
|
speed = ent->yaw_speed;
|
||
|
if (ideal_yaw > current_yaw)
|
||
|
{
|
||
|
if (move >= 180)
|
||
|
move = move - 360;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (move <= -180)
|
||
|
move = move + 360;
|
||
|
}
|
||
|
if (move > 0)
|
||
|
{
|
||
|
if (move > speed)
|
||
|
move = speed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (move < -speed)
|
||
|
move = -speed;
|
||
|
}
|
||
|
ent->s.angles[YAW] = anglemod (current_yaw + move);
|
||
|
}
|
||
|
|
||
|
// Pitch
|
||
|
if (current_pitch != ideal_pitch)
|
||
|
{
|
||
|
move = ideal_pitch - current_pitch;
|
||
|
speed = ent->yaw_speed;
|
||
|
if (ideal_pitch > current_pitch)
|
||
|
{
|
||
|
if (move >= 180)
|
||
|
move = move - 360;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (move <= -180)
|
||
|
move = move + 360;
|
||
|
}
|
||
|
if (move > 0)
|
||
|
{
|
||
|
if (move > speed)
|
||
|
move = speed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (move < -speed)
|
||
|
move = -speed;
|
||
|
}
|
||
|
ent->s.angles[PITCH] = anglemod (current_pitch + move);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Set bot to move to it's movetarget. (following node path)
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
void ACEMV_MoveToGoal(edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
// If a rocket or grenade is around deal with it
|
||
|
// Simple, but effective (could be rewritten to be more accurate)
|
||
|
if(strcmp(self->movetarget->classname,"rocket")==0 ||
|
||
|
strcmp(self->movetarget->classname,"grenade")==0 ||
|
||
|
strcmp(self->movetarget->classname,"homing rocket")==0)
|
||
|
|
||
|
{
|
||
|
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
if(debug_mode)
|
||
|
debug_printf("%s: Oh crap a rocket!\n",self->client->pers.netname);
|
||
|
|
||
|
// strafe left/right
|
||
|
if(rand()%1 && ACEMV_CanMove(self, MOVE_LEFT))
|
||
|
ucmd->sidemove = -400;
|
||
|
else if(ACEMV_CanMove(self, MOVE_RIGHT))
|
||
|
ucmd->sidemove = 400;
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Set bot's movement direction
|
||
|
VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
ucmd->forwardmove = 400;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Main movement code. (following node path)
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
void ACEMV_Move(edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
vec3_t dist;
|
||
|
int current_node_type=-1;
|
||
|
int next_node_type=-1;
|
||
|
int i;
|
||
|
|
||
|
// Get current and next node back from nav code.
|
||
|
if(!ACEND_FollowPath(self))
|
||
|
{
|
||
|
self->state = STATE_WANDER;
|
||
|
self->wander_timeout = level.time + 1.0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
current_node_type = nodes[self->current_node].type;
|
||
|
next_node_type = nodes[self->next_node].type;
|
||
|
|
||
|
///////////////////////////
|
||
|
// Move To Goal
|
||
|
///////////////////////////
|
||
|
if (self->movetarget)
|
||
|
ACEMV_MoveToGoal(self,ucmd);
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
// Grapple
|
||
|
///////////////////////////////////////////////////////
|
||
|
if(next_node_type == NODE_GRAPPLE)
|
||
|
{
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
ACEIT_ChangeWeapon(self,FindItem("grapple"));
|
||
|
ucmd->buttons = BUTTON_ATTACK;
|
||
|
return;
|
||
|
}
|
||
|
// Reset the grapple if hangin on a graple node
|
||
|
if(current_node_type == NODE_GRAPPLE)
|
||
|
{
|
||
|
CTFPlayerResetGrapple(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
// Platforms
|
||
|
///////////////////////////////////////////////////////
|
||
|
if(current_node_type != NODE_PLATFORM && next_node_type == NODE_PLATFORM)
|
||
|
{
|
||
|
// check to see if lift is down?
|
||
|
for(i=0;i<num_items;i++)
|
||
|
if(item_table[i].node == self->next_node)
|
||
|
if(item_table[i].ent && // Knightmare- fix crash
|
||
|
item_table[i].ent->moveinfo.state != STATE_BOTTOM) // crashes here
|
||
|
return; // Wait for elevator
|
||
|
}
|
||
|
if(current_node_type == NODE_PLATFORM && next_node_type == NODE_PLATFORM)
|
||
|
{
|
||
|
// Move to the center
|
||
|
self->move_vector[2] = 0; // kill z movement
|
||
|
if(VectorLength(self->move_vector) > 10)
|
||
|
ucmd->forwardmove = 200; // walk to center
|
||
|
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
return; // No move, riding elevator
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
// Jumpto Nodes
|
||
|
///////////////////////////////////////////////////////
|
||
|
if(next_node_type == NODE_JUMP ||
|
||
|
(current_node_type == NODE_JUMP && next_node_type != NODE_ITEM && nodes[self->next_node].origin[2] > self->s.origin[2]))
|
||
|
{
|
||
|
// Set up a jump move
|
||
|
ucmd->forwardmove = 400;
|
||
|
ucmd->upmove = 400;
|
||
|
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
VectorCopy(self->move_vector,dist);
|
||
|
VectorScale(dist,440,self->velocity);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
// Ladder Nodes
|
||
|
///////////////////////////////////////////////////////
|
||
|
if(next_node_type == NODE_LADDER && nodes[self->next_node].origin[2] > self->s.origin[2])
|
||
|
{
|
||
|
// Otherwise move as fast as we can
|
||
|
ucmd->forwardmove = 400;
|
||
|
self->velocity[2] = 320;
|
||
|
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
// If getting off the ladder
|
||
|
if(current_node_type == NODE_LADDER && next_node_type != NODE_LADDER &&
|
||
|
nodes[self->next_node].origin[2] > self->s.origin[2])
|
||
|
{
|
||
|
ucmd->forwardmove = 400;
|
||
|
ucmd->upmove = 200;
|
||
|
self->velocity[2] = 200;
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
// Water Nodes
|
||
|
///////////////////////////////////////////////////////
|
||
|
if(current_node_type == NODE_WATER)
|
||
|
{
|
||
|
// We need to be pointed up/down
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
// If the next node is not in the water, then move up to get out.
|
||
|
if(next_node_type != NODE_WATER && !(gi.pointcontents(nodes[self->next_node].origin) & MASK_WATER)) // Exit water
|
||
|
ucmd->upmove = 400;
|
||
|
|
||
|
ucmd->forwardmove = 300;
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
// Falling off ledge?
|
||
|
if(!self->groundentity)
|
||
|
{
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
self->velocity[0] = self->move_vector[0] * 360;
|
||
|
self->velocity[1] = self->move_vector[1] * 360;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Check to see if stuck, and if so try to free us
|
||
|
// Also handles crouching
|
||
|
if(VectorLength(self->velocity) < 37)
|
||
|
{
|
||
|
// Keep a random factor just in case....
|
||
|
if(random() > 0.1 && ACEMV_SpecialMove(self, ucmd))
|
||
|
return;
|
||
|
|
||
|
self->s.angles[YAW] += random() * 180 - 90;
|
||
|
|
||
|
ucmd->forwardmove = 400;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Otherwise move as fast as we can
|
||
|
ucmd->forwardmove = 400;
|
||
|
|
||
|
ACEMV_ChangeBotAngle(self);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Wandering code (based on old ACE movement code)
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
void ACEMV_Wander(edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
vec3_t temp;
|
||
|
|
||
|
// Do not move
|
||
|
if(self->next_move_time > level.time)
|
||
|
return;
|
||
|
|
||
|
// Special check for elevators, stand still until the ride comes to a complete stop.
|
||
|
if(self->groundentity != NULL && self->groundentity->use == Use_Plat)
|
||
|
if(self->groundentity->moveinfo.state == STATE_UP ||
|
||
|
self->groundentity->moveinfo.state == STATE_DOWN) // only move when platform not
|
||
|
{
|
||
|
self->velocity[0] = 0;
|
||
|
self->velocity[1] = 0;
|
||
|
self->velocity[2] = 0;
|
||
|
self->next_move_time = level.time + 0.5;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Is there a target to move to
|
||
|
if (self->movetarget)
|
||
|
ACEMV_MoveToGoal(self,ucmd);
|
||
|
|
||
|
////////////////////////////////
|
||
|
// Swimming?
|
||
|
////////////////////////////////
|
||
|
VectorCopy(self->s.origin,temp);
|
||
|
temp[2]+=24;
|
||
|
|
||
|
if(gi.pointcontents (temp) & MASK_WATER)
|
||
|
{
|
||
|
// If drowning and no node, move up
|
||
|
if(self->client->next_drown_time > 0)
|
||
|
{
|
||
|
ucmd->upmove = 1;
|
||
|
self->s.angles[PITCH] = -45;
|
||
|
}
|
||
|
else
|
||
|
ucmd->upmove = 15;
|
||
|
|
||
|
ucmd->forwardmove = 300;
|
||
|
}
|
||
|
else
|
||
|
self->client->next_drown_time = 0; // probably shound not be messing with this, but
|
||
|
|
||
|
////////////////////////////////
|
||
|
// Lava?
|
||
|
////////////////////////////////
|
||
|
temp[2]-=48;
|
||
|
if(gi.pointcontents(temp) & (CONTENTS_LAVA|CONTENTS_SLIME))
|
||
|
{
|
||
|
// safe_bprintf(PRINT_MEDIUM,"lava jump\n");
|
||
|
self->s.angles[YAW] += random() * 360 - 180;
|
||
|
ucmd->forwardmove = 400;
|
||
|
ucmd->upmove = 400;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(ACEMV_CheckEyes(self,ucmd))
|
||
|
return;
|
||
|
|
||
|
// Check for special movement if we have a normal move (have to test)
|
||
|
if(VectorLength(self->velocity) < 37)
|
||
|
{
|
||
|
if(random() > 0.1 && ACEMV_SpecialMove(self,ucmd))
|
||
|
return;
|
||
|
|
||
|
self->s.angles[YAW] += random() * 180 - 90;
|
||
|
|
||
|
if(!M_CheckBottom(self) || !self->groundentity) // if there is ground continue otherwise wait for next move
|
||
|
ucmd->forwardmove = 400;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ucmd->forwardmove = 400;
|
||
|
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
// Attack movement routine
|
||
|
//
|
||
|
// NOTE: Very simple for now, just a basic move about avoidance.
|
||
|
// Change this routine for more advanced attack movement.
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
void ACEMV_Attack (edict_t *self, usercmd_t *ucmd)
|
||
|
{
|
||
|
float c;
|
||
|
vec3_t target;
|
||
|
vec3_t angles;
|
||
|
|
||
|
// Randomly choose a movement direction
|
||
|
c = random();
|
||
|
|
||
|
if(c < 0.2 && ACEMV_CanMove(self,MOVE_LEFT))
|
||
|
ucmd->sidemove -= 400;
|
||
|
else if(c < 0.4 && ACEMV_CanMove(self,MOVE_RIGHT))
|
||
|
ucmd->sidemove += 400;
|
||
|
|
||
|
if(c < 0.6 && ACEMV_CanMove(self,MOVE_FORWARD))
|
||
|
ucmd->forwardmove += 400;
|
||
|
else if(c < 0.8 && ACEMV_CanMove(self,MOVE_FORWARD))
|
||
|
ucmd->forwardmove -= 400;
|
||
|
|
||
|
if(c < 0.95)
|
||
|
ucmd->upmove -= 200;
|
||
|
else
|
||
|
ucmd->upmove += 200;
|
||
|
|
||
|
// Set the attack
|
||
|
ucmd->buttons = BUTTON_ATTACK;
|
||
|
|
||
|
// Aim
|
||
|
VectorCopy(self->enemy->s.origin,target);
|
||
|
|
||
|
// modify attack angles based on accuracy (mess this up to make the bot's aim not so deadly)
|
||
|
// target[0] += (random()-0.5) * 20;
|
||
|
// target[1] += (random()-0.5) * 20;
|
||
|
|
||
|
// Set direction
|
||
|
VectorSubtract (target, self->s.origin, self->move_vector);
|
||
|
vectoangles (self->move_vector, angles);
|
||
|
VectorCopy(angles,self->s.angles);
|
||
|
|
||
|
// if(debug_mode)
|
||
|
// debug_printf("%s attacking %s\n",self->client->pers.netname,self->enemy->client->pers.netname);
|
||
|
}
|