|
Title:
Putting BitchBots into your Mod
Author: Maj.Bitch
Date: Feb 8, 2000
Difficulty: Not too bad but tedious..
Email: peblair@hotmail.com
Note: Please give credit where credit is due.
=====================================================
First off, alot of this
bot is Maj.Bitch's modification
of the code developed by Ponpoko for his 3zb2 bot and much
credit goes to him/her for their absolutely brilliant work!
Okay.. It seems like alot
of code but with cutting and
pasting you should be able to have this ALL added to
your source and compiled and running in around 10-15
minutes.. Be patient.. Follow the tutorial step-by-step
and be methodical so you don't leave anything out.
The bots use the *.chn
file format used by the 3zb2 bots.
I decided to stick with this file format because there
are a ton of route files already in existence which you
can download from the web so it saves everybody alot of
time. Goto: http://www.btinternet.com/~RsFHQ/ and ENTER
the site. Then goto the "3rd Zigock" pull-down menu across
the top of the site and click on "info & download". Work
over to the Route Database. There, you'll find a route
file for ALOT of maps. Any of these maps will work with
this bot.
The bot source will be
looking for the route files in
"C:\Quake2\3zb2\chdtm\" directory (or you can change the
path in the bot.c file).. Set it up anyway you like so
long as the bot can access the *.chn route files..
Compile your gamex86.dll
and run it just like you would do
normally. At the console, type "sv addbot 5" to add 5
bots to the game (for example).. I've got the source
set up for 50 different bots you can put into the game
even at the same time if you want absolute total mayhem!
The source will randomly select bots from the list of 50
so feel free to change the names/skins to whatever you like..
Spawn around 40 bots and
then go into spectator mode and
roam around and watch the CHAOS!
Also, the code will
automatically respawn the same bots back
into the game as your next map cycles thru.
As always, you should
feel free to modify the bot's chat
and insult sayings to fit your particular mod.. Just
change the text strings in bot.c (easy to do) and the
bots will say the things that you want them too.. I've
got it setup so that you can have the bots say things
when they accidentally kill themselves, or when they
frag somebody, or just random chatter!
Okay.. About this
tutorial...
Simply open the
appropriate file and find the EXACT spot
that I'm referencing and then cut the source immediately
after the Maj++ and stop cutting at the next dashed line..
Okay, Let's get started!
=====================================================
Open your g_local.h file and make these changes..
<C&NBSP;CODE>
#define IT_KEY 16
#define IT_POWERUP 32
// Maj++ ----------------------------- - START CUT HERE
#define IT_PACK 64
#define IT_HEALTH 128
#define IT_NODE 256
//------------------------------------ - END CUT HERE
</C&NBSP;CODE>
Further down in this same
file after the gitem_t struct:
<C&NBSP;CODE>
char *precaches;
} gitem_t;
// Maj++----------------------------------
// Item Ammo (IT_AMMO)
gitem_t *item_shells,
*item_cells,
*item_rockets,
*item_grenades,
*item_slugs,
*item_bullets,
// Item Weapons (IT_WEAPON)
*item_blaster,
*item_shotgun,
*item_machinegun,
*item_supershotgun,
*item_chaingun,
*item_handgrenade,
*item_grenadelauncher,
*item_rocketlauncher,
*item_hyperblaster,
*item_railgun,
*item_bfg,
// Item Armor (IT_ARMOR)
*item_jacketarmor,
*item_combatarmor,
*item_bodyarmor,
*item_armorshard,
*item_powerscreen,
*item_powershield,
// Item Health (IT_HEALTH)
*item_adrenaline,
*item_health,
*item_stimpak,
*item_health_large,
*item_health_mega,
// Item Powerup (IT_POWERUP)
*item_quad,
*item_invulnerability,
*item_silencer,
*item_breather,
*item_enviro,
// Item Pak (IT_PACK)
*item_pack,
*item_bandolier,
// Item Nodes (IT_NODE)
*item_navi1,
*item_navi2,
*item_navi3;
//-------------------------------------
</C&NBSP;CODE>
Further, at the bottom of
your client_persistant_t struct add:
<C&NBSP;CODE>
qboolean spectator;
//Maj++-------------------------
int botindex;
int routeindex;
//------------------------------
} client_persistant_t;
</C&NBSP;CODE>
Further, at the bottom of
your gclient_t struct add:
<C&NBSP;CODE>
edict_t *chase_target; // player we are chasing
qboolean update_chase; // need to update chase info?
//Maj++ --------------------------------------
float changetime; //next time to alter bot's parameters..
float chattime; //time of next chat
float insulttime; //time of next insult
float taunttime; //time of next taunting;
int movestate; //movement state
int combatstate; //combat state
float duckedtime; //next ducked time
edict_t *current_enemy; //current enemy target
edict_t *prev_enemy; //old enemy
edict_t *waiting_obj; //ent bot is waiting on (elevators, etc).
int ground_contents; //contents under bot
float ground_slope; //slope under bot
qboolean havetarget; //got a target? (yes/no)
int enemysearchcnt; //enemy search count
int waterstate; //current water state (0..2)
qboolean routetrace; //
int routeindex; //route[] index
float routelocktime; //route[] locking time
float routereleasetime; //route[] release time
vec3_t targ_old_origin; //target's old origin
int battlemode; //current battle mode - see FIRE_*
int battlecount; //temp battle count
int battlesubcnt; //subcount
int battleduckcnt; //ducktime during battle
int enemy_routeindex; //index of target
float targetlock; //enemy locking time
vec3_t movtarget_pt; //moving target waiting point
float nextcheck; //checking time
vec3_t my_old_origin; //bot's old origin
qboolean objshot; //button has been activated
float moveyaw; //temp moving yaw
vec3_t vtemp; //temp storage vec
float camptime; //time spent camping
gitem_t *campitem; //item camping near
vec3_t lastorigin; //origin of camping spot
//----------------------------------------------------
};
</C&NBSP;CODE>
Further, at the bottom of
your edict_t struct add:
<C&NBSP;CODE>
// common data blocks
moveinfo_t moveinfo;
monsterinfo_t monsterinfo;
//Maj++ ----------------------------------
qboolean isabot;
edict_t *union_ent;
edict_t *trainteam;
//----------------------------------------
};
</C&NBSP;CODE>
At the very bottom of
your g_local.h file add:
<C&NBSP;CODE>
#include "bot.h" // Maj++
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Now, let's open your q_shared.h file and add:
<C&NBSP;CODE>
#define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_SLIME|CONTENTS_LAVA)
#define MASK_SHOT (CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEADMONSTER)
//Maj++------------------
#define MASK_BOTSOLID (CONTENTS_SOLID|CONTENTS_LADDER|CONTENTS_WINDOW|CONTENTS_MONSTER)
#define MASK_BOTSOLIDX (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTER)
//----------------------
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_combat.c and add:
<C&NBSP;CODE>
void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
{
gclient_t *client;
int take;
int save;
int asave;
int psave;
int te_sparks;
if (!targ->takedamage)
return;
CheckBotCrushed(targ,inflictor,mod); // Maj++ - Add this line here
</C&NBSP;CODE>
Further down add:
<C&NBSP;CODE>
if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
{
if (OnSameTeam (targ, attacker))
{
if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
damage = 0;
else
mod |= MOD_FRIENDLY_FIRE;
}
// Maj++ ---------------------------------------
else
if (targ->client && !targ->isabot)
if (attacker->client)
targ->client->current_enemy = attacker;
//----------------------------------------------------------
}
meansOfDeath = mod;
</C&NBSP;CODE>
Further down add:
<C&NBSP;CODE>
// do the damage
if (take)
{
if ((targ->svflags & SVF_MONSTER) || (client)) { // Maj++ - left brace
SpawnDamage (TE_BLOOD, point, normal, take);
BotCheckEnemy(client,attacker,targ,mod); } // Maj++
else
SpawnDamage (te_sparks, point, normal, take);
targ->health = targ->health - take;
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_func.c and add this:
At the bottom of your
SP_Func_Plat() add:
<C&NBSP;CODE>
ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
bFuncPlat(ent); // Maj++ - Add this line here
}
</C&NBSP;CODE>
At the bottom of your
SP_Func_Button() add:
<C&NBSP;CODE>
VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
gi.linkentity (ent);
bFuncButton(ent); // Maj++ - Add this line here
}
</C&NBSP;CODE>
In the middle of your
door_blocked() function, add:
<C&NBSP;CODE>
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
bDoorBlocked(self); // Maj++ - Add this line here
if (self->spawnflags & DOOR_CRUSHER)
return;
</C&NBSP;CODE>
At the bottom of your
SP_func_door() add:
<C&NBSP;CODE>
ent->nextthink = level.time + FRAMETIME;
if (ent->health || ent->targetname)
ent->think = Think_CalcMoveSpeed;
else
ent->think = Think_SpawnDoorTrigger;
bFuncDoor(ent); // Maj++ - Add this line here
}
</C&NBSP;CODE>
Near the bottom of your
SP_func_train() add:
<C&NBSP;CODE>
gi.linkentity(self);
bFuncTrain(self); // Maj++ - Add this line here.
if (self->target)
{
// start trains on the second frame, to make sure their targets have had
// a chance to spawn
self->nextthink = level.time + FRAMETIME;
self->think = func_train_find;
}
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_misc.c file and add this stuff:
At the top of your
teleporter_touch(), add:
<C&NBSP;CODE>
void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
edict_t *dest;
int i;
if (!other->client)
return;
dest = G_Find (NULL, FOFS(targetname), self->target);
if (!dest)
{
return;
}
ForceRouteReset(other); // Maj++ - Add this line here
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_monster.c file and add this:
Comment out (or delete)
your M_CheckGround() function and
substitute this one in its place OR you can make the
indicated changes... Your choice.
<C&NBSP;CODE>
void M_CheckGround (edict_t *ent)
{
vec3_t point;
trace_t trace;
if (ent->flags & (FL_SWIM|FL_FLY))
return;
ResetGroundSlope(ent); // Maj++
if (ent->velocity[2] > 100)
{
ent->groundentity = NULL;
return;
}
// if the hull point one-quarter unit down is solid the entity is on ground
point[0] = ent->s.origin[0];
point[1] = ent->s.origin[1];
point[2] = ent->s.origin[2] - 0.25;
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_BOTSOLID); // Maj++ - Change mask
// check steepness
if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
{
ent->groundentity = NULL;
return;
}
/* Maj++ - Comment out this stuff here.
if (!trace.startsolid && !trace.allsolid)
{
VectorCopy (trace.endpos, ent->s.origin);
ent->groundentity = trace.ent;
ent->groundentity_linkcount = trace.ent->linkcount;
ent->velocity[2] = 0;
}
*/
TraceAllSolid(ent,point,trace); // Maj++ - Add this line here
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_phys.c file and make these changes..
In the middle of your
SV_Push() function, add:
<C&NBSP;CODE>
// see if any solid entities are inside the final position
check = g_edicts+1;
for (e = 1; e < globals.num_edicts; e++, check++)
{
if (!check->inuse)
continue;
//Maj++ ---------------------------------------------------------
if (!check->solid) continue;
if (check->classname[0] == 'R')
if (check->item != item_navi2) continue;
//-----------------------------------------------------------------
if (check->movetype == MOVETYPE_PUSH
|| check->movetype == MOVETYPE_STOP
|| check->movetype == MOVETYPE_NONE
|| check->movetype == MOVETYPE_NOCLIP)
continue;
</C&NBSP;CODE>
At the top of your
SV_Physics_Step() function add:
<C&NBSP;CODE>
void SV_Physics_Step (edict_t *ent)
{
qboolean wasonground;
qboolean hitsound = false;
float *vel;
float speed, newspeed, control;
float friction;
edict_t *groundentity;
int mask;
// airborn monsters should always check for ground
if (!ent->groundentity)
M_CheckGround (ent);
//Maj++ --------------------------------
else if (ent->isabot)
M_CheckGround (ent); // bots must check ground
//--------------------------------------
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_save.c file and make this one change:
<C&NBSP;CODE>
void InitGame (void)
{
gi.dprintf ("==== InitGame ====\n");
srand(time(NULL)); // Maj++ - Init random generator here
gun_x = gi.cvar ("gun_x", "0", 0);
gun_y = gi.cvar ("gun_y", "0", 0);
gun_z = gi.cvar ("gun_z", "0", 0);
</C&NBSP;CODE>
At the very bottom of
this same function add:
<C&NBSP;CODE>
LoadBotNames(); // Maj++ - Add this line here
</C&NBSP;CODE>
Okay, we're done with this file now. Close it out
=====================================================
Open your g_spawn.c file and make this one change:
At the bottom of you
SpawnEntities() function, add;
<C&NBSP;CODE>
gi.dprintf ("%i entities inhibited\n", inhibit);
G_FindTeams ();
G_FindTrainTeam(); // Maj++ - Add these lines here
ReadRouteFile(); // Maj++
RespawnAllBots(); // Maj++
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_svcmds.c file and make this one change:
At the bottom of your
ServerCommand() function add:
<C&NBSP;CODE>
//Maj++ -------------------------------
if (!strcmp(gi.argv(1),"addbot"))
SpawnNumBots(atoi(gi.argv(2)));
else if (!strcmp(gi.argv(1),"clearall"))
RemoveAllBots();
//-------------------------------------
else
gi_cprintf (NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd);
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_utils.c file and add these new functions:
<C&NBSP;CODE>
// Maj++ - BEFORE you paste in these new printf functions you
// MUST do a global substitution for these function names thru
// your entire source! We need to preven bots from getting
// printed messages sent to them..
//
// Change all gi.cprintf to gi_cprintf.
// Change all gi.centerprintf to gi_centerprintf
// Change all gi,bprintf to gi_bprintf
//
// THEN PASTE THESE FUNCTIONS INTO YOUR SOURCE
//==============================================
void gi_cprintf(edict_t *ent, int printlevel, char *fmt, ...) {
char bigbuffer[0x10000];
va_list argptr;
int len;
if (!ent || !ent->inuse || !ent->client) return;
if (ent->isabot) return; // Maj++ - bots don't get messages
va_start(argptr,fmt);
len = vsprintf(bigbuffer,fmt,argptr);
va_end(argptr);
gi.cprintf(ent, printlevel, bigbuffer);
}
//===================================================
void gi_centerprintf(edict_t *ent, char *fmt, ...) {
char bigbuffer[0x10000];
va_list argptr;
int len;
if (!ent || !ent->inuse || !ent->client) return;
if (ent->isabot) return; // Maj++ - bots don't get messages
va_start(argptr,fmt);
len = vsprintf(bigbuffer,fmt,argptr);
va_end(argptr);
gi.centerprintf(ent, bigbuffer);
}
//===================================================
void gi_bprintf(int printlevel, char *fmt, ...) {
char bigbuffer[0x10000];
int i, len;
va_list argptr;
edict_t *ent;
va_start(argptr,fmt);
len = vsprintf(bigbuffer,fmt,argptr);
va_end(argptr);
if (dedicated->value)
gi_cprintf(NULL, printlevel, bigbuffer);
for (i=0; i<game.maxclients; i++) {
ent=g_edicts+1+i;
gi_cprintf(ent, printlevel, bigbuffer); }
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your p_hud.c file and make these changes:
At the bottom of your
MoveClientToIntermission(), add:
<C&NBSP;CODE>
ent->s.sound = 0;
ent->solid = SOLID_NOT;
if (ent->isabot) return; // Maj++ - don't unicast() to bots!
// add the layout
if (deathmatch->value || coop->value)
{
DeathmatchScoreboardMessage (ent, NULL);
gi.unicast (ent, true);
}
}
</C&NBSP;CODE>
Add this to your
DeathmatchScoreboard() function:
<C&NBSP;CODE>
void DeathmatchScoreboard (edict_t *ent)
{
if (ent->isabot) return; // Maj++ - Don't unicast() to bots!
DeathmatchScoreboardMessage (ent, ent->enemy);
gi.unicast (ent, true);
}
</C&NBSP;CODE>
At the top of your
Cmd_Help_f() function add:
<C&NBSP;CODE>
void Cmd_Help_f (edict_t *ent)
{
if (ent->isabot) return; // Maj++ - bots don't need help..
// this is for backwards compatability
if (deathmatch->value)
{
Cmd_Score_f (ent);
return;
}
</C&NBSP;CODE>
At the top of your
G_CheckChaseStats() function add:
<C&NBSP;CODE>
void G_CheckChaseStats (edict_t *ent)
{
int i;
gclient_t *cl;
if (ent->isabot) return; // Maj++ - bots don't chasecam
</C&NBSP;CODE>
At the top of your
G_SetSpectatorStats() add:
<C&NBSP;CODE>
void G_SetSpectatorStats (edict_t *ent)
{
gclient_t *cl = ent->client;
if (ent->isabot) return; // Maj++ - bots don't spectate
if (!cl->chase_target)
G_SetStats (ent);
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your p_view.c file and make these changes:
In the middle of your
ClientEndServerFrame() function add:
<C&NBSP;CODE>
// burn from lava, etc
P_WorldEffects ();
//
// set model angles from view angles so other things in
// the world can tell which direction you are looking
//
if (!ent->isabot) { // Maj++ - Add this line here
if (ent->client->v_angle[0] > 180)
ent->s.angles[0] = (-360+ent->client->v_angle[0])*0.33;
else
ent->s.angles[0] = ent->client->v_angle[0]*0.33;
ent->s.angles[1] = ent->client->v_angle[1];
ent->s.angles[2] = SV_CalcRoll(ent->s.angles,ent->velocity)*4;
} // Maj++ - Add this end brace here
</C&NBSP;CODE>
Further down in this same
function make this change:
<C&NBSP;CODE>
//
// calculate speed and cycle to be used for
// all cyclic walking effects
//
xyspeed = sqrt(ent->velocity[0]*ent->velocity[0] + ent->velocity[1]*ent->velocity[1]);
SetBotXYSpeed(ent,&xyspeed); // Maj++ - Add this line here
</C&NBSP;CODE>
At the bottom of this
same function add:
<C&NBSP;CODE>
if (ent->isabot) return; // Maj++ - don't unicast to bots!
// if the scoreboard is up, update it
if (ent->client->showscores && !(level.framenum & 31) )
{
DeathmatchScoreboardMessage (ent, ent->enemy);
gi.unicast (ent, false);
}
}
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your p_weapon.c file and make this change:
At the top of your
Pickup_Weapon() function add:
<C&NBSP;CODE>
qboolean Pickup_Weapon (edict_t *ent, edict_t *other)
{
int index;
gitem_t *ammo;
index = ITEM_INDEX(ent->item);
CheckCampSite(ent,other); // Maj++ - Add this line here
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your p_client.c file and make these changes:
At the top of your
ClientObituary() function add:
<C&NBSP;CODE>
void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
{
int mod;
char *message=NULL; // Maj++ - BugFix
char *message2=""; // Maj++ - BugFix
qboolean ff;
</C&NBSP;CODE>
Now, at the very bottom
of this same function add:
<C&NBSP;CODE>
gi_bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
if (deathmatch->value)
{
TauntVictim(attacker,self); // Maj++ - Add this line here
InsultVictim(attacker,self); // Maj++ - Add this line here
if (ff)
attacker->client->resp.score--;
else
attacker->client->resp.score++;
}
</C&NBSP;CODE>
At the top of your
LookAtKiller() function add:
<C&NBSP;CODE>
void LookAtKiller(edict_t *self, edict_t *inflictor, edict_t *attacker)
{
vec3_t dir;
if (self->isabot) return; // Maj++ - Add this line here
</C&NBSP;CODE>
In the middle of your
player_die() function change:
<C&NBSP;CODE>
self->svflags |= SVF_DEADMONSTER;
if (!self->deadflag)
{
self->client->respawn_time=level.time + 1.0;
// Maj++ --------------------------------------------
if (self->isabot)
self->client->respawn_time += 4.0; // Increase to 4+1=5 for bots
//---------------------------------------------------
LookAtKiller (self, inflictor, attacker);
self->client->ps.pmove.pm_type = PM_DEAD;
ClientObituary(self, inflictor, attacker);
</C&NBSP;CODE>
Further down in this same
function add:
<C&NBSP;CODE>
self->client->breather_framenum = 0;
self->client->enviro_framenum = 0;
self->flags &= ~FL_POWER_ARMOR;
if (self->health < -40 || !self->isabot) // Maj++ - Make this quick change
{ // gib
gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
for (n= 0; n < 4; n++)
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
</C&NBSP;CODE>
At the bottom of your
SelectSpawnPoint() function add:
<C&NBSP;CODE>
VectorCopy (spot->s.origin, origin);
origin[2] += 9;
if (ent->isabot) origin[2] += 21; // Maj++ - Add this line here
VectorCopy (spot->s.angles, angles);
}
</C&NBSP;CODE>
At the top of your
respawn() function, add this:
<C&NBSP;CODE>
void respawn (edict_t *self)
{
if (deathmatch->value || coop->value)
{
// spectator's don't leave bodies
if (self->movetype != MOVETYPE_NOCLIP)
CopyToBodyQue (self);
self->svflags &= ~SVF_NOCLIENT;
if (self->isabot) return; // Maj++ - Add this line here
PutClientInServer (self);
</C&NBSP;CODE>
In the middle of your
PutClientInServer() function add:
<C&NBSP;CODE>
ent->s.angles[PITCH] = 0;
ent->s.angles[YAW] = spawn_angles[YAW];
ent->s.angles[ROLL] = 0;
VectorCopy (ent->s.angles, client->ps.viewangles);
VectorCopy (ent->s.angles, client->v_angle);
SetBotThink(ent); // Maj++ - Add this line here
// spawn a spectator
if (client->pers.spectator) {
client->chase_target = NULL;
client->resp.spectator = true;
</C&NBSP;CODE>
Comment out your
ClientThink() function and substitute
this function for it OR make the indicated change.
<C&NBSP;CODE>
// Maj++ - This ClientThink allows chasecaming of bots
// Original Source did not. ?? Haven't taken time to
// see exactly what diff was between the two. But...
void ClientThink(edict_t *ent, usercmd_t *ucmd){
edict_t *other;
int i, j=0;
pmove_t pm;
level.current_entity = ent;
if (level.intermissiontime) {
ent->client->ps.pmove.pm_type = PM_FREEZE;
if (level.time > level.intermissiontime+5.0)
if (ucmd->buttons & BUTTON_ANY)
level.exitintermission = true;
return; }
if (ent->isabot) return; // Maj++ - Add this line here
pm_passent = ent;
if (ent->client->chase_target) {
ent->client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
ent->client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
ent->client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); }
else {
memset(&pm, 0, sizeof(pm));
if (ent->movetype == MOVETYPE_NOCLIP)
ent->client->ps.pmove.pm_type = PM_SPECTATOR;
else if (ent->s.modelindex != 255)
ent->client->ps.pmove.pm_type = PM_GIB;
else if (ent->deadflag)
ent->client->ps.pmove.pm_type = PM_DEAD;
else
ent->client->ps.pmove.pm_type = PM_NORMAL;
ent->client->ps.pmove.gravity = 800;
pm.s = ent->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(&ent->client->old_pmove, &pm.s, sizeof(pm.s)))
pm.snapinitial = true;
pm.cmd = *ucmd;
pm.trace = PM_trace;
pm.pointcontents = gi.pointcontents;
gi.Pmove(&pm);
ent->client->ps.pmove = pm.s;
ent->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);
ent->client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
ent->client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
ent->client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
if (ent->groundentity && !pm.groundentity)
if ((pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
gi.sound(ent, 2, gi.soundindex("*jump1.wav"), 1, 1, 0);
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){
ent->client->ps.viewangles[2] = 40;
ent->client->ps.viewangles[0] = -15;
ent->client->ps.viewangles[1] = ent->client->killer_yaw;}
else{
VectorCopy(pm.viewangles, ent->client->v_angle);
VectorCopy(pm.viewangles, ent->client->ps.viewangles);}
gi.linkentity(ent);
if (ent->movetype != MOVETYPE_NOCLIP)
G_TouchTriggers(ent);
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;
if (!other->touch) continue;
other->touch(other, ent, NULL, NULL);}}
ent->client->oldbuttons = ent->client->buttons;
ent->client->buttons = ucmd->buttons;
ent->client->latched_buttons |= ent->client->buttons & ~ent->client->oldbuttons;
if (ent->client->latched_buttons & BUTTON_ATTACK){
if (ent->client->resp.spectator) {
ent->client->latched_buttons = 0;
if (ent->client->chase_target) {
ent->client->chase_target = NULL;
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;}
else
GetChaseTarget(ent);}
else if (!ent->client->weapon_thunk) {
ent->client->weapon_thunk = true;
Think_Weapon(ent);}}
if (ent->client->resp.spectator) {
if (ucmd->upmove >= 10) {
if (!(ent->client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
ent->client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
if (ent->client->chase_target)
ChaseNext(ent);
else
GetChaseTarget(ent);}}
else
ent->client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;}
for (i=1; i <= game.maxclients; i++) {
other = g_edicts+i;
if (other->inuse && ent->client->chase_target == other)
UpdateChaseCam(ent);}
}
</C&NBSP;CODE>
In the middle of your
ClientBeginServerFrame() add:
<C&NBSP;CODE>
if (deathmatch->value &&
client->pers.spectator != client->resp.spectator &&
(level.time - client->respawn_time) >= 5) {
spectator_respawn(ent);
return;
}
if (ent->isabot) SV_Physics_Step(ent); // Maj++ - Add this line here
// run weapon animations if it hasn't been done by a ucmd_t
if (!client->weapon_thunk && !client->resp.spectator)
Think_Weapon (ent);
else
client->weapon_thunk = false;
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Open your g_items.c file and make these changes:
Near the bottom of your
DoRespawn() function add:
<C&NBSP;CODE>
ent->svflags &= ~SVF_NOCLIENT;
ent->solid = SOLID_TRIGGER;
gi.linkentity (ent);
if (ent->classname[0] == 'R') return; // Maj++ - Add this line here
// send an effect
ent->s.event = EV_ITEM_RESPAWN;
}
</C&NBSP;CODE>
In the middle of your
Pickup_Health() function add:
<C&NBSP;CODE>
if (ent->style & HEALTH_TIMED)
{
CheckCampSite(ent,other); // Maj++ - Add this line here..
ent->think = MegaHealth_think;
ent->nextthink = level.time + 5;
ent->owner = other;
</C&NBSP;CODE>
In the middle of your Touch_Item() function add:
<C&NBSP;CODE>
// change selected item
if (ent->item->use)
other->client->pers.selected_item = other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX(ent->item);
CheckPrimaryWeapon(ent,other); // Maj++ - Add this line right here
if (ent->classname[0] != 'R') // Maj++ - Add this line here to nest if statement
if (ent->item->pickup == Pickup_Health)
{
</C&NBSP;CODE>
At the top of your itemlist[] array add:
<C&NBSP;CODE>
gitem_t itemlist[] =
{
{
NULL
}, // leave index 0 alone
//Maj++ ----------------------------------------
//
// NAVIGATION NODES
//
{
"R_navi1",
Pickup_Navi,
NULL,
NULL,
NULL,
NULL,
"models/items/keys/spinner/tris.md2", 0,
NULL,
NULL,
"Roam Navi1",
2,
10,
NULL,
IT_NODE,
0,
NULL,
0,
""
},
{
"R_navi2",
Pickup_Navi,
NULL,
NULL,
NULL,
NULL,
"models/items/keys/spinner/tris.md2", 0,
NULL,
NULL,
"Roam Navi2",
2,
30,
NULL,
IT_NODE,
0,
NULL,
0,
""
},
{
"R_navi3",
Pickup_Navi,
NULL,
NULL,
NULL,
NULL,
"models/items/keys/spinner/tris.md2", 0,
NULL,
NULL,
"Roam Navi3",
2,
20,
NULL,
IT_NODE,
0,
NULL,
0,
""
},
//---------------------------------------------------
</C&NBSP;CODE>
Further in your
itemlist[] array make these changes too..
<C&NBSP;CODE>
/* icon */ "p_adrenaline",
/* pickup */ "Adrenaline",
/* width */ 2,
60,
NULL,
IT_HEALTH, // Maj++ - Change to this flag
0,
NULL,
0,
""
},
/* icon */ "p_bandolier",
/* pickup */ "Bandolier",
/* width */ 2,
60,
NULL,
IT_PACK, // Maj++ - Change to this flag
0,
NULL,
0,
""
},
/* icon */ "i_pack",
/* pickup */ "Ammo Pack",
/* width */ 2,
180,
NULL,
IT_PACK, // Maj++ - Change to this flag
0,
NULL,
0,
""
},
/* icon */ "i_health",
/* pickup */ "Health",
/* width */ 3,
0,
NULL,
IT_HEALTH, // Maj++ - Change to this flag here..
0,
NULL,
0,
"items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav"
},
// end of list marker
{NULL}
};
</C&NBSP;CODE>
Now, in your
SetItemNames() function add:
<C&NBSP;CODE>
void SetItemNames (void)
{
int i;
gitem_t *it;
for (i=0 ; i<game.num_items ; i++)
{
it = &itemlist[i];
gi.configstring (CS_ITEMS+i, it->pickup_name);
}
InitAllItems(); // Maj++ - put this here...
</C&NBSP;CODE>
Okay, we're done with
this file now. Close it out
=====================================================
Now, CREATE a new file called bot.h and paste this:
<C&NBSP;CODE>
//Maj++--------------------------------------------
#define MAXNODES 10000
typedef struct {
vec3_t Pt;
union {
vec3_t Tcourner;
unsigned short linkpod[6]; };
edict_t *ent;
short index;
short state;
} route_t;
extern route_t Route[MAXNODES];
extern int TotalRouteNodes;
#define MAXBOTS 51 // Number of name/skin entries
typedef struct {
char netname[21]; //netname
char skin[64]; //skin
int ingame; //spawned
int skill[6]; //parameters
} botinfo_t;
extern botinfo_t Bot[MAXBOTS+1];
void gi_cprintf(edict_t *ent, int printlevel, char *fmt, ...);
void gi_centerprintf(edict_t *ent, char *fmt, ...);
void gi_bprintf(int printlevel, char *fmt, ...);
void Use_Plat(edict_t *ent, edict_t *other, edict_t *activator);
void train_use(edict_t *self, edict_t *other, edict_t *activator);
void button_use(edict_t *self, edict_t *other, edict_t *activator);
void door_use(edict_t *self, edict_t *other, edict_t *activator);
void rotating_use(edict_t *self, edict_t *other, edict_t *activator);
void trigger_relay_use(edict_t *self, edict_t *other, edict_t *activator);
void plat_go_up(edict_t *ent);
void SV_Physics_Step(edict_t *ent);
void path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
void DoRespawn(edict_t *ent);
void Cmd_Kill_f(edict_t *ent);
void ClientUserinfoChanged(edict_t *ent, char *userinfo);
void ClientDisconnect(edict_t *ent);
void Use_Item(edict_t *ent, edict_t *other, edict_t *activator);
//weapon
int WeapIndex(int weap);
void Weapon_Blaster (edict_t *ent);
void Weapon_Shotgun (edict_t *ent);
void Weapon_SuperShotgun (edict_t *ent);
void Weapon_Machinegun (edict_t *ent);
void Weapon_Chaingun (edict_t *ent);
void Weapon_HyperBlaster (edict_t *ent);
void Weapon_RocketLauncher (edict_t *ent);
void Weapon_Grenade (edict_t *ent);
void Weapon_GrenadeLauncher (edict_t *ent);
void Weapon_Railgun (edict_t *ent);
void Weapon_BFG (edict_t *ent);
void LoadBotNames(void);
int GetKindWeapon(gitem_t *it);
void ReadRouteFile(void);
void droptofloor2(edict_t *ent);
void SpawnNumBots(int num);
void RespawnAllBots(void);
void TauntVictim(edict_t *ent, edict_t *victim);
void InsultVictim(edict_t *ent, edict_t *victim);
qboolean Pickup_Navi(edict_t *ent, edict_t *other);
void BotThink(edict_t *ent);
void CheckCampSite(edict_t *ent,edict_t *other);
void bFuncPlat(edict_t *ent);
void bFuncButton(edict_t *ent);
void bDoorBlocked(edict_t *ent);
void bFuncDoor(edict_t *ent);
void bFuncTrain(edict_t *ent);
void ResetGroundSlope(edict_t *ent);
void TraceAllSolid(edict_t *ent, vec3_t point, trace_t tr);
void CheckPrimaryWeapon(edict_t *ent, edict_t *other);
void G_FindTrainTeam(void);
void ForceRouteReset(edict_t *other);
void InitAllItems(void);
float SetBotXYSpeed(edict_t *ent, float *xyspeed);
void SetBotThink(edict_t *ent);
void CheckBotCrushed(edict_t *targ,edict_t *inflictor,int mod);
void BotCheckEnemy(gclient_t *client, edict_t *attacker, edict_t *targ, int mod);
void RemoveAllBots(void);
void BotCheckGrapple(edict_t *ent);
//---------------------------------------------------
</C&NBSP;CODE>
Save this new file...
=====================================================
LASTLY!! CREATE a new file called bot.c and paste:
<C&NBSP;CODE>
//Maj++----------------------------------------------
#include "g_local.h"
#include "m_player.h"
// Special thanks to Ponpoko for his brilliant work upon
// which this work is based.
#define MaxOf(x,y) ((x)>(y)?(x):(y))
#define DEG2RAD(a) ((a)*(M_PI/180.0F))
//skill[]
#define AIMACCURACY 0 // 0..9 aiming accuracy
#define AGGRESSION 1 // 0..9
#define COMBATSKILL 2 // 0..9
#define VRANGEVIEW 3 // 60-120 deg - Vertical view range
#define HRANGEVIEW 4 // 60-120 deg - Horizontal view range
#define PRIMARYWEAP 5 // 1..11 - 1=Blaster, 11=BFG
//=====================================================
//=====================================================
vec3_t zvec = {0,0,0};
float myrandom=0.5F;
int TotalRouteNodes=0;
route_t Route[MAXNODES];
botinfo_t Bot[MAXBOTS+1];
qboolean pickup_priority=false;
int trace_priority=0;
float JumpMax=0.0F;
int NumBotsInGame=0; // [1..MAXBOTS]
int SkillLevel[10] = {
0x00001000|0x10000000|0x00000002|0x00000004,
0x00001000|0x10000000|0x00000002|0x00000004,
0x00001000|0x10000000|0x00000002,
0x00001000|0x10000000|0x00000002,
0x00001000|0x10000000,
0x00001000|0x10000000,
0x00001000|0x10000000,
0x00001000|0x00000010,
0x10000000|0x00000010,
0x10000000|0x00000010
};
typedef char cfg[64];
typedef cfg cfg_t[2];
cfg_t gbot[] = { // ENTRIES MUST == MAXBOTS, else kabooom!
{ "Tyr574", "cyborg/tyr574" },
{ "Razor", "male/razor" },
{ "Cobalt", "female/cobalt" },
{ "Scout", "male/scout" },
{ "ps9000", "cyborg/ps9000" },
{ "Brianna", "female/brianna" },
{ "Recon", "male/recon" },
{ "Viper", "male/viper" },
{ "oni911", "cyborg/oni911" },
{ "Flak", "male/flak" },
{ "Venus", "female/venus" },
{ "Pointman", "male/pointman" },
{ "Stiletto", "female/stiletto" },
{ "Claymore", "male/claymore" },
{ "Jezebel", "female/jezebel" },
{ "Cypher", "male/cypher" },
{ "Athena", "female/athena" },
{ "Major", "male/major" },
{ "Jungle", "female/jungle" },
{ "Howitzer", "male/howitzer" },
{ "Ensign", "female/ensign" },
{ "NightOps", "male/nightops" },
{ "Psycho", "male/psycho" },
{ "Voodoo", "female/voodoo" },
{ "Rampage", "male/rampage" },
{ "Brazen", "cyborg/tyr574" },
{ "Zeroid", "male/razor" },
{ "Busted", "female/cobalt" },
{ "Lotus", "female/lotus" },
{ "Grunt", "male/grunt" },
{ "Flyte", "female/venus" },
{ "Avenger", "male/pointman" },
{ "WetSpot", "female/stiletto" },
{ "Bomber", "male/claymore" },
{ "Shaved", "female/jezebel" },
{ "BadBoy", "male/cypher" },
{ "Nailz", "female/athena" },
{ "Sarge", "male/major" },
{ "Hairless", "female/jungle" },
{ "PlowBoy", "male/howitzer" },
{ "T1000", "cyborg/tyr574" },
{ "Copper", "male/razor" },
{ "Queen", "female/cobalt" },
{ "Private", "male/scout" },
{ "z111x", "cyborg/ps9000" },
{ "Virgin", "female/brianna" },
{ "Sniper", "male/recon" },
{ "Violator", "male/viper" },
{ "rs3434", "cyborg/oni911" },
{ "Metal", "male/flak" }, // Count=50
{ "Killer", "male/grunt" }, // 1 extra for safety
};
//===================================
//===================================
//======================================================
//========== BASIC BOT UTILITY FUNCTIONS ===============
//======================================================
//======================================================
qboolean G_EntExists(edict_t *ent) {
return ((ent) && (ent->client) && (ent->inuse));
}
//======================================================
qboolean G_ClientNotDead(edict_t *ent) {
qboolean buried=true;
qboolean b1=(ent->client->ps.pmove.pm_type!=PM_DEAD);
qboolean b2=(ent->deadflag==DEAD_NO);
qboolean b3=(ent->health > 0);
return ((b3 || b2 || b1) && buried);
}
//======================================================
qboolean G_ClientInGame(edict_t *ent) {
if (!G_EntExists(ent)) return false;
if (!G_ClientNotDead(ent)) return false;
if (ent->client->pers.spectator) return false;
return (ent->client->respawn_time + 5.0 < level.time);
}
//==============================================
float Get_yaw(vec3_t vec) {
vec3_t out;
double yaw;
VectorCopy(vec,out);
out[2] = 0;
VectorNormalize(out);
yaw = (double)(acos((double)out[0]))/M_PI*180;
if (asin((double)out[1])<0) yaw *= -1;
return (float)yaw;
}
//==============================================
float Get_pitch(vec3_t vec) {
vec3_t out;
float pitch;
VectorCopy(vec,out);
VectorNormalize(out);
pitch = (((float)(acos((double)out[2])))/M_PI*180)-90;
return (float)((pitch<-180)?(pitch+360):pitch);
}
//======================================================
void AdjustAngle(edict_t *ent, vec3_t targaim, float aim) {
VectorSet(ent->s.angles,(Get_pitch(targaim)),(Get_yaw(targaim)),0.0F);
ent->s.angles[1] += aim*0.70*(myrandom-0.5);
if (ent->s.angles[1] > 180)
ent->s.angles[1] -= 360;
else
if (ent->s.angles[1] < -180)
ent->s.angles[1] += 360;
ent->s.angles[0] += aim*0.70*(myrandom-0.5);
if (ent->s.angles[0] > 90)
ent->s.angles[0] = 90;
else
if (ent->s.angles[0] < -90)
ent->s.angles[0] = -90;
}
//=============================================
qboolean BankCheck(edict_t *ent, vec3_t pos) {
trace_t tr;
vec3_t end;
VectorCopy(pos,end);
end[2] -= 4096;
tr = gi.trace(pos, ent->mins, ent->maxs, end, ent, MASK_BOTSOLIDX);
return !(tr.startsolid || tr.allsolid || tr.plane.normal[2] < 0.8);
}
//=============================================
qboolean HazardCheck(edict_t *ent, vec3_t pos) {
trace_t tr;
vec3_t end;
int contents;
VectorCopy(pos,end);
end[2] -= 8192;
contents = (ent->client->enviro_framenum>level.framenum)?CONTENTS_LAVA:(CONTENTS_LAVA|CONTENTS_SLIME);
tr = gi.trace(pos, ent->mins, ent->maxs, end, ent, MASK_OPAQUE);
return !(tr.contents & contents);
}
//==============================================
void SetBotAnim(edict_t *ent) {
gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
if (ent->client->anim_priority >= ANIM_JUMP) return;
ent->s.frame = FRAME_jump1-1;
ent->client->anim_end = FRAME_jump6;
}
//============================================================
qboolean Get_FlyingSpeed(float bottom,float block,float dist,float *speed) {
float tdist;
if (bottom >= 40) {
if (block > 4) return false;
tdist = (dist*block)*.250; }
else if (bottom >= 35) {
if (block > 5) return false;
tdist = (dist*block)*.200; }
else if (bottom >= 30) {
if (block > 6) return false;
tdist = (dist*block)*.167; }
else if (bottom >= 20) {
if (block > 7) return false;
tdist = (dist*block)*.143; }
else if (bottom >= -5) {
if (block > 8) return false;
tdist = (dist*block)*.125; }
else if (bottom >= -20) {
if (block > 9) return false;
tdist = (dist*block)*.143; }
else if (bottom >= -35) {
if (block > 10) return false;
tdist = (dist*block)*.167; }
else if (bottom >= -52) {
if (block > 11) return false;
tdist = (dist*block)*.200; }
else if (bottom >= -75) {
if (block > 12) return false;
tdist = (dist*block)*.250; }
else if (bottom >= -95) {
if (block > 13) return false;
tdist = (dist*block)*.333; }
else if (bottom >=-125) {
if (block > 14) return false;
tdist = (dist*block)*.500; }
else {
if (block > 15) return false;
tdist = (dist*block)*.500; }
*speed = tdist/30;
return true;
}
//==========================================
float SetBotXYSpeed(edict_t *ent, float *xyspeed) {
if (!ent->isabot) return *xyspeed;
if (ent->groundentity && ent->client->movestate & (0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800)) {
*xyspeed = (VectorLength(ent->groundentity->velocity)<1)?300:0;
if (*xyspeed)
ent->client->movestate |= 0x00000010; } // Don't move
else
*xyspeed = (ent->client->camptime > level.time)?0:300;
return *xyspeed;
}
//==========================================
void SetBotThink(edict_t *ent) {
if (!ent->isabot) return;
ent->client->chattime = level.time+60+(20*rand()%6);
ent->client->ping = atoi(Info_ValueForKey(ent->client->pers.userinfo,"ping"));
ent->think = BotThink;
ent->nextthink = level.time+0.1;
}
//==========================================
void ForceRouteReset(edict_t *other) {
if (!other->isabot) return;
if (!other->client->routetrace) return;
if (other->client->pers.routeindex < TotalRouteNodes) {
if (Route[other->client->pers.routeindex].state == 2)
other->client->pers.routeindex++;
if (other->client->pers.routeindex < TotalRouteNodes)
if (Route[other->client->pers.routeindex].state == 22)
other->client->pers.routeindex++; }
}
//==========================================
void G_FindTrainTeam(void) {
edict_t *e;
int i;
e = &g_edicts[(int)maxclients->value+1];
for (i=maxclients->value+1; i<globals.num_edicts; i++, e++) {
if (e->inuse && e->classname) {
if (e->touch == path_corner_touch && e->targetname && e->target) {
qboolean findteam = false;
char *currtarget = e->target; //target
char *currtargetname = e->targetname; //targetname
int k,lc=0,loopindex=0;
edict_t *teamlist[1025];
char *targethist[1024];
memset(&teamlist,0,sizeof(teamlist));
memset(&targethist,0,sizeof(targethist));
targethist[0] = e->targetname;
while (lc < MAX_EDICTS) {
int j;
edict_t *p,*t=&g_edicts[(int)maxclients->value+1];
for (j=maxclients->value+1; j<globals.num_edicts; j++, t++)
if (t->inuse && t->classname && !t->trainteam)
if (t->use == train_use)
if (!stricmp(t->target,currtargetname))
for (k=0;k<lc;k++)
if (teamlist[k] == t)
break;
if (k == lc) {
teamlist[lc] = t;
lc++; }
p = G_PickTarget(currtarget);
if (!p) break; // exit while
currtarget = p->target;
currtargetname = p->targetname;
if (!p->target) break; // exit while
for (k=0;k<loopindex;k++)
if (!stricmp(targethist[k],currtargetname))
break;
if (k < loopindex) {
findteam = true;
break; }
targethist[loopindex] = currtargetname;
loopindex++; }
if (findteam && lc > 0)
for (k=0;k<lc;k++) {
if (!teamlist[k+1]) {
teamlist[k]->trainteam = teamlist[0];
break; }
teamlist[k]->trainteam = teamlist[k+1]; } } } }
}
//==============================================
void droptofloor2(edict_t *ent) {
vec3_t trmin,trmax,min,mins,maxs;
float i,j=0,yaw;
trace_t tr;
vec3_t v,dest;
VectorSet(ent->mins,-15,-15,-15);
VectorSet(ent->maxs,8,8,15);
if (ent->union_ent && ent->item == item_navi2) {
dest[0] = (ent->union_ent->s.origin[0]+ent->union_ent->mins[0]+ent->union_ent->s.origin[0]+ent->union_ent->maxs[0])*0.5;
dest[1] = (ent->union_ent->s.origin[1]+ent->union_ent->mins[1]+ent->union_ent->s.origin[1]+ent->union_ent->maxs[1])*0.5;
for (i=ent->union_ent->s.origin[2]+ent->union_ent->mins[2]; i <= ent->union_ent->s.origin[2]+ent->union_ent->maxs[2] +16; i++) {
dest[2] = i;
tr = gi.trace(dest,ent->mins,ent->maxs,dest,ent,MASK_SOLID);
if ((!tr.startsolid && !tr.allsolid) && j==1) {
j = 2; break; }
else
if ((tr.startsolid || tr.allsolid) && (!j) && tr.ent == ent->union_ent)
j = 1; }
VectorCopy(dest,ent->s.origin);
VectorSubtract(ent->s.origin,ent->union_ent->s.origin,ent->moveinfo.dir); }
ent->s.modelindex = 0;
ent->solid = (ent->item == item_navi3)?SOLID_NOT:SOLID_TRIGGER;
ent->movetype = MOVETYPE_TOSS;
ent->touch = Touch_Item;
ent->use = NULL;
VectorSet(v,0,0,-128);
VectorAdd(ent->s.origin, v, dest);
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
if (tr.startsolid && ent->item == item_navi1) {
G_FreeEdict(ent);
return; }
VectorCopy(tr.endpos, ent->s.origin);
if (ent->team) {
ent->flags &= ~FL_TEAMSLAVE;
ent->chain = ent->teamchain;
ent->teamchain = NULL;
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
if (ent == ent->teammaster) {
ent->nextthink = level.time+FRAMETIME;
ent->think = DoRespawn; } }
if (ent->spawnflags & 2) {
ent->solid = SOLID_BBOX;
ent->touch = NULL;
ent->s.effects &= ~EF_ROTATE;
ent->s.renderfx &= ~RF_GLOW; }
if (ent->spawnflags & 1) {
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
ent->use = Use_Item; }
gi.linkentity(ent);
VectorCopy(ent->s.origin,min);
VectorSet(mins, -15, -15, -15);
VectorSet(maxs, 8, 8, 0);
min[2] -= 128;
for (i=0;i<8;i++) {
if (i<4) {
yaw = DEG2RAD(90*i-180);
for (j=32;j<80;j+=2) {
trmin[0] = ent->s.origin[0]+cos(yaw)*j;
trmin[1] = ent->s.origin[1]+sin(yaw)*j;
trmin[2] = ent->s.origin[2];
VectorCopy(trmin,trmax);
trmax[2] -= 128;
tr = gi.trace(trmin, mins, maxs, trmax, ent, MASK_PLAYERSOLID);
if (tr.endpos[2] < ent->s.origin[2]-16 && tr.endpos[2] > min[2])
if (!tr.allsolid && !tr.startsolid) {
min[2] = tr.endpos[2];
min[0] = ent->s.origin[0]+cos(yaw)*(j+16);
min[1] = ent->s.origin[1]+sin(yaw)*(j+16);
break; } } }
else {
yaw = DEG2RAD(90*(i-4)-135);
for (j=32;j<80;j+=2) {
trmin[0] = ent->s.origin[0]+cos(yaw)*46;
trmin[1] = ent->s.origin[1]+sin(yaw)*46;
trmin[2] = ent->s.origin[2];
VectorCopy(trmin,trmax);
trmax[2] -= 128;
tr = gi.trace(trmin, NULL, NULL, trmax, ent, MASK_PLAYERSOLID);
if (tr.endpos[2] < ent->s.origin[2]-16 && tr.endpos[2] > min[2])
if (!tr.allsolid && !tr.startsolid) {
VectorCopy(tr.endpos,min);
break; } } } }
VectorCopy(min,ent->moveinfo.start_origin);
}
//==============================================
void TraceAllSolid(edict_t *ent, vec3_t point, trace_t tr) {
if (tr.allsolid) {
vec3_t stp,v1,v2;
trace_t tracep;
VectorSet(v1,-16,-16,-24);
VectorSet(v2,16,16,4);
VectorCopy(ent->s.origin,stp);
stp[2] += 24;
tracep = gi.trace(stp, v1, v2, point, ent, MASK_BOTSOLID);
if (tracep.ent && !tracep.allsolid)
if (tracep.ent->classname[0] == 'f') {
VectorCopy(tracep.endpos,ent->s.origin);
ent->groundentity = tracep.ent;
ent->groundentity_linkcount = tracep.ent->linkcount;
gi.linkentity(ent);
return; } }
else {
if (ent->client) {
ent->client->ground_contents = tr.contents;
ent->client->ground_slope = tr.plane.normal[2]; }
VectorCopy(tr.endpos, ent->s.origin);
ent->groundentity = tr.ent;
ent->groundentity_linkcount = tr.ent->linkcount; }
gi.linkentity(ent);
}
//==============================================
void ResetGroundSlope(edict_t *ent) {
if (!ent->isabot) return;
ent->client->ground_slope = 1.0;
}
//==============================================
void SpawnItem3(edict_t *it_ent, gitem_t *item) {
it_ent->item = item;
it_ent->s.effects = 0;
it_ent->s.renderfx = 0;
it_ent->s.modelindex = 0;
it_ent->nextthink = level.time+0.2;
it_ent->think = droptofloor2;
}
//===============================================
void bFuncTrain(edict_t *self) {
gitem_t *it = item_navi1;
edict_t *it_ent = G_Spawn();
VectorAdd(self->s.origin,self->mins,self->monsterinfo.last_sighting);
it_ent->classname = it->classname;
it_ent->union_ent = self;
self->union_ent = it_ent;
SpawnItem3(it_ent,it);
}
//===============================================
void bFuncDoor(edict_t *ent) {
VectorAdd(ent->s.origin,ent->mins,ent->monsterinfo.last_sighting);
if (fabs(ent->moveinfo.start_origin[2]-ent->moveinfo.end_origin[2]) > 20) {
gitem_t *it=item_navi3;
edict_t *it_ent=G_Spawn();
it_ent->classname = it->classname;
it_ent->union_ent = ent;
ent->union_ent = it_ent;
SpawnItem3(it_ent,it); }
}
//===============================================
void bDoorBlocked(edict_t *self) {
int i;
for (i=1; i<=maxclients->value; i++) {
edict_t *ent = &g_edicts[i];
if (!ent->isabot) continue;
if (ent->client->waiting_obj != self) continue;
if (!ent->client->movestate) continue;
ent->client->movestate |= 0x00000010; }
}
//===============================================
void bFuncButton(edict_t *ent) {
vec3_t tdir,tdir2;
vec3_t abs_movedir;
float dist=1;
gitem_t *it=item_navi2;
edict_t *it_ent=G_Spawn();
VectorAdd(ent->s.origin,ent->mins,ent->monsterinfo.last_sighting);
it_ent->classname = it->classname;
VectorCopy(ent->s.origin,it_ent->s.origin);
it_ent->s.origin[0] = (ent->absmin[0]+ent->absmax[0])*0.5;
it_ent->s.origin[1] = (ent->absmin[1]+ent->absmax[1])*0.5;
it_ent->s.origin[2] = (ent->absmin[2]+ent->absmax[2])*0.5;
it_ent->union_ent = ent;
ent->union_ent = it_ent;
VectorSubtract(ent->moveinfo.start_origin, ent->moveinfo.end_origin, abs_movedir);
VectorNormalize(abs_movedir);
while (dist < 500) {
VectorScale(abs_movedir, dist, tdir);
VectorAdd(it_ent->s.origin,tdir,tdir2);
if (!(gi.pointcontents(tdir2) & CONTENTS_SOLID)) break;
dist++; }
VectorScale(abs_movedir,(dist+20), tdir);
VectorAdd(it_ent->s.origin,tdir,tdir2);
VectorCopy(tdir2,it_ent->s.origin);
it_ent->item = it;
it_ent->s.effects = 0;
it_ent->s.renderfx = 0;
it_ent->s.modelindex = 0;
it_ent->solid = SOLID_TRIGGER;
it_ent->movetype = MOVETYPE_NONE;
it_ent->touch = Touch_Item;
gi.linkentity(it_ent);
}
//===============================================
void bFuncPlat(edict_t *ent) {
gitem_t *it=item_navi1;
edict_t *it_ent=G_Spawn();
VectorAdd(ent->s.origin,ent->mins,ent->monsterinfo.last_sighting);
it_ent->classname = it->classname;
it_ent->union_ent = ent;
ent->union_ent = it_ent;
SpawnItem3(it_ent,it);
}
//===============================================
void CheckBotCrushed(edict_t *targ,edict_t *inflictor,int mod) {
if (!targ->isabot || mod != MOD_CRUSH) return;
if ((targ->client->waiting_obj == inflictor && targ->client->movestate) || targ->groundentity == inflictor)
targ->client->movestate |= 0x00000010;
}
//===============================================
void CheckPrimaryWeapon(edict_t *ent, edict_t *other) {
if (!other->isabot) return;
if (ent->item->use) {
// Switch weapon if picked up primary
int k = GetKindWeapon(ent->item);
if (k != WEAP_BLASTER) {
if (Bot[other->client->pers.botindex].skill[6] == k)
ent->item->use(other,ent->item); } }
}
//===============================================
void BotCheckEnemy(gclient_t *client, edict_t *attacker, edict_t *targ, int mod) {
if (client && targ->isabot && attacker) {
if (client->battlemode & 0x00000008)
client->battlemode &= ~0x00000008;
if (mod & MOD_RAILGUN|MOD_ROCKET|MOD_BLASTER|MOD_HYPERBLASTER) {
if (attacker->client && !client->current_enemy)
if (!OnSameTeam(targ, attacker))
client->current_enemy = attacker; } }
}
//===============================================
void CheckCampSite(edict_t *ent, edict_t *other) {
if (!other->isabot) return;
if (ent->item != item_health_mega && ent->item != item_railgun) return;
if (ent->item == item_health_mega && other->client->pers.weapon == item_blaster)
return;
if (other->client->quad_framenum > level.framenum) return;
if (other->client->camptime >= level.time) return;
if (random() > 0.60) return; // camp 60% of time
other->client->camptime = level.time+20+rand()%10; // 20..30
other->client->chattime = level.time+10+rand()%15; // 10..25
other->client->taunttime = other->client->camptime+10; // turn OFF!
VectorCopy(ent->s.origin, other->client->lastorigin);
other->client->lastorigin[2] += 16;
other->client->campitem = ent->item; //camping near this item
}
//======================================================
//======== ROUTE FILE AND BOT CONFIGURATION ============
//======================================================
//===============================================
void RandomizeParameters(int i) {
Bot[i].skill[AIMACCURACY] = 5+rand()%4; // [5..9]
Bot[i].skill[AGGRESSION] = 6+rand()%3; // [6..9]
Bot[i].skill[COMBATSKILL] = 3+rand()%6; // [3..9]
Bot[i].skill[PRIMARYWEAP] = 3+rand()%8; // [3..11]
Bot[i].skill[VRANGEVIEW] = 60+10*rand()%6; // [60..120]
Bot[i].skill[HRANGEVIEW] = 60+10*rand()%6; // [60..120]
}
//==============================================
void LoadBotNames(void) {
int i;
for (i=0;i<MAXBOTS;i++) {
memset(&Bot[i],0,sizeof(botinfo_t));
sprintf(Bot[i].netname,"%s",gbot[i][0]);
sprintf(Bot[i].skin,"%s",gbot[i][1]);
RandomizeParameters(i); }
}
//==============================================
//==============================================
int CurrentIndex=0;
//==================================================
qboolean RTJump_Chk(vec3_t apos,vec3_t tpos) {
float x,l,vel,yori;
vec3_t v,vv;
int mf = 0;
vel = 300;
yori = apos[2];
VectorSubtract(tpos,apos,v);
for(x = 1;x <= 60;++x ) {
vel -= sv_gravity->value * 0.1;
yori += vel * 0.1;
if(vel > 0) {
if(mf == 0) {
if(tpos[2] < yori) mf = 2; } }
else if(x > 1) {
if(mf == 0) {
if(tpos[2] < yori) mf = 2; }
else if(mf == 2) {
if(tpos[2] >= yori) {
mf = 1;
break; } } } }
VectorCopy(v,vv);
vv[2] = 0;
l = VectorLength(vv);
if(x > 1) l /= (x - 1);
return (l < 32 && mf==1);
}
//==============================================
void G_FindRouteLink(edict_t *ent) {
trace_t rs_trace;
qboolean tpbool;
int i,j,k,l,total = 0;
vec3_t v;
float x;
if (JumpMax == 0) {
x = 300-ent->gravity*sv_gravity->value*0.1;
JumpMax = 0;
while (1) {
JumpMax += x*0.1;
x -= ent->gravity*sv_gravity->value*0.1;
if (x < 0) break; } }
for(i = 0;i < CurrentIndex;i++) {
if(Route[i].state == 0) {
for(j=0;j<CurrentIndex;j++) {
if(abs(i-j) <= 50 || j==i || Route[j].state != 0) continue;
VectorSubtract(Route[j].Pt,Route[i].Pt,v);
if(v[2] > JumpMax || v[2] < -500) continue;
v[2] = 0;
if(VectorLength(v) > 200) continue;
if(fabs(v[2]) > 20 || VectorLength(v) > 64)
if(!RTJump_Chk(Route[i].Pt,Route[j].Pt))
continue;
tpbool = false;
for(l = -5;l < 6;l++) {
if((i+l)<0 || (i+l)>=CurrentIndex) continue;
for(k = 0;k < 5;k++) {
if(!Route[i + l].linkpod[k]) break;
if(abs(Route[i + l].linkpod[k] - j) < 50) {
tpbool = true;
break; } }
if(tpbool) break; }
if(tpbool) continue;
rs_trace = gi.trace(Route[j].Pt,NULL,NULL,Route[i].Pt,ent,MASK_SOLID);
if(!rs_trace.startsolid && !rs_trace.allsolid && rs_trace.fraction == 1.0) {
for(k = 0;k < 5;k++) {
if(!Route[i].linkpod[k]) {
Route[i].linkpod[k] = j;
total++;
break; } } } } } }
G_FreeEdict (ent);
}
//==================================================
void ReadRouteFile(void) {
int i,j;
vec3_t v;
edict_t *e;
FILE *fp;
char name[256];
TotalRouteNodes = 0;
sprintf(name,"c:/quake2/3zb2/chdtm/%s.chn",level.mapname);
if (fp=fopen(name,"rb")) {
char code[8];
unsigned int size;
CurrentIndex=0;
memset(Route,0,sizeof(route_t));
memset(code,0,8);
fread(code,sizeof(char),8,fp);
fread(&CurrentIndex,sizeof(int),1,fp);
size = (unsigned int)CurrentIndex*sizeof(route_t);
fread(Route,size,1,fp);
fclose(fp);
TotalRouteNodes = CurrentIndex;
gi.dprintf("%d Total Route Nodes\n",TotalRouteNodes); }
if (TotalRouteNodes==0) {
gi.dprintf("NO ROUTE FILE LOADED\n");
return; }
for(i = 0;i < TotalRouteNodes;i++) {
if((Route[i].state > 2 && Route[i].state <= 7)
|| Route[i].state == -10 || Route[i].state == -11) {
edict_t *other = &g_edicts[(int)maxclients->value+1];
for (j=maxclients->value+1 ; j<globals.num_edicts ; j++, other++) {
if (other && other->inuse) {
if(Route[i].state == 4
|| Route[i].state == 5
|| Route[i].state == 7
|| Route[i].state == 6) {
VectorAdd(other->s.origin,other->mins,v);
if(VectorCompare (Route[i].Pt,v)) {
if(Route[i].state == 4 && !Q_stricmp(other->classname, "func_plat")) {
Route[i].ent = other;
break; }
else if(Route[i].state == 5 && !Q_stricmp(other->classname, "func_train")) {
Route[i].ent = other;
break; }
else if(Route[i].state == 7 && !Q_stricmp(other->classname, "func_button")) {
Route[i].ent = other;
break; }
else if(Route[i].state == 6 && !Q_stricmp(other->classname, "func_door")) {
Route[i].ent = other;
break; } } }
else
if(Route[i].state == 3 || Route[i].state == -10 || Route[i].state == -11) {
if(VectorCompare(Route[i].Pt,other->monsterinfo.last_sighting)) {
Route[i].ent = other;
break; } } } }
if(j >= globals.num_edicts)
Route[i].state = 0; } }
e = G_Spawn();
e->nextthink = level.time + FRAMETIME * 2;
e->think = G_FindRouteLink;
}
//======================================================
//============= SPAWNING BOTS INTO THE GAME ============
//======================================================
//======================================================
char *Random_IP(void) {
static char ipstr[16];
int ip1;
do {
ip1 = 128+(rand()%128);
} while (ip1 == 192 || ip1 == 172);
sprintf(ipstr, "%d.%d.%d.%d", ip1, (int)(rand()%256), (int)(rand()%256), (int)(rand()%256));
return ipstr;
}
//=============================================
int GetFreeEdict(void) {
int i;
for (i=(int)(game.maxclients-1); i>=0; i--) {
edict_t *ent = g_edicts+i+1;
if (!ent->inuse) {
G_InitEdict(ent);
return i; } }
return -1; // refuse connection
}
//======================================================
void G_MuzzleFlash(short rec_no, vec3_t origin, int flashnum) {
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(rec_no);
gi.WriteByte(flashnum);
gi.multicast(origin, MULTICAST_PVS);
}
//=============================================
qboolean SpawnBot(int i) {
edict_t *ent;
int clientnum;
char userinfo[512];
if (i<0 || i>(MAXBOTS-1)) return false;
clientnum = GetFreeEdict();
if (clientnum < 0) {
gi.dprintf("Server is full. Increase maxclients!!\n");
return false; }
ent = g_edicts+clientnum+1;
ent->client = &game.clients[clientnum];
ent->isabot = true;
InitClientResp(ent->client);
InitClientPersistant(ent->client);
ent->client->pers.botindex = i;
ent->client->pers.routeindex = 0;
sprintf(userinfo,"\\name\\%s\\skin\\%s\\fov\\90\\hand\\2\\ip\\%s\\ping\\%3d", Bot[i].netname, Bot[i].skin, Random_IP(), (int)(100+(rand()%128))); // Maj++ - We store ping in userinfo string. hehe
ClientUserinfoChanged(ent,userinfo);
PutClientInServer(ent);
G_MuzzleFlash((short)(ent-g_edicts), ent->s.origin, (int)(MZ_LOGIN));
gi_bprintf(2,"%s entered the game\n",Bot[i].netname);
ClientEndServerFrame(ent);
return true;
}
//==================================================
void RemoveAllBots(void) {
int i;
for (i=1;i<=game.maxclients;i++) {
edict_t *ent = &g_edicts[i];
if (!ent || !ent->isabot) continue;
Bot[ent->client->pers.botindex].ingame = 0;
ent->isabot=false;
ClientDisconnect(ent); }
NumBotsInGame = 0; // Must Reset
}
//==================================================
void SpawnNumBots(int numbots) {
int j,k;
for (k=1;k<=numbots;k++) {
do {
j=(int)(rand()%(MAXBOTS-1));
} while (Bot[j].ingame);
if (!SpawnBot(j)) return; // No free edicts!!
Bot[j].ingame = 1;
NumBotsInGame++; }
if (NumBotsInGame >= MAXBOTS)
gi_cprintf(NULL,2,"Max Bots Spawned.\n");
}
//=============================================
void PutBotInGame(edict_t *ent) {
if (ent->count >= MAXBOTS) {
G_FreeEdict(ent);
return; }
if (Bot[ent->count].ingame)
SpawnBot(ent->count);
ent->count += 1;
ent->nextthink = level.time + 0.2; // Interval setting
}
//=============================================
// Temp edict to put bots into game
//=============================================
void RespawnAllBots(void) {
edict_t *ent = G_Spawn();
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_NOT;
ent->count = 0;
ent->nextthink = level.time + 0.1;
ent->think = PutBotInGame;
}
//=======================================================
//============ TAUNTING/CHATTING/INSULTING ==============
//=======================================================
//==============================================
void InsultVictim(edict_t *ent, edict_t *victim) {
if (!ent->isabot) return;
if (!victim || !victim->client) return;
if (ent->client->insulttime > level.time) return;
// if bot just killed self then...
if (victim == ent) {
if (myrandom < 0.40)
switch (rand()%7) {
case 0: gi_bprintf(3,"%s: OUCH!!!\n", ent->client->pers.netname); break;
case 1: gi_bprintf(3,"%s: I hate that!\n", ent->client->pers.netname); break;
case 2: gi_bprintf(3,"%s: shit!\n", ent->client->pers.netname); break;
case 3: gi_bprintf(3,"%s: not again!\n", ent->client->pers.netname); break;
case 4: gi_bprintf(3,"%s: Ugghhhh!\n", ent->client->pers.netname); break;
case 5: gi_bprintf(3,"%s: wtf!\n", ent->client->pers.netname); break;
case 6: gi_bprintf(3,"%s: Ohhhhh!!!!\n", ent->client->pers.netname); } }
else
// Otherwise, chat
if (myrandom < 0.80)
switch (rand()%19) {
case 0: gi_bprintf(3,"%s: You REALLY suck!\n", ent->client->pers.netname); break;
case 1: gi_bprintf(3,"%s: YOU SUCK!\n", ent->client->pers.netname); break;
case 2: gi_bprintf(3,"%s: Suck THIS!\n", ent->client->pers.netname); break;
case 3: gi_bprintf(3,"%s: This sucks!\n", ent->client->pers.netname); break;
case 4: gi_bprintf(3,"%s: Eat Me!\n", ent->client->pers.netname); break;
case 5: gi_bprintf(3,"%s: You ALL suck!\n", ent->client->pers.netname); break;
case 6: gi_bprintf(3,"%s: Suck THAT\n", ent->client->pers.netname); break;
case 7: gi_bprintf(3,"%s: Muhhhhaahhhaaa\n", ent->client->pers.netname); break;
case 8: gi_bprintf(3,"%s: Muhaaaaaaaaa!!\n", ent->client->pers.netname); break;
case 9: gi_bprintf(3,"%s: Huuuhhhaaaaaa!\n", ent->client->pers.netname); break;
case 10:gi_bprintf(3,"%s: Muhhhhhhaaaaa!!!\n", ent->client->pers.netname); break;
case 11:gi_bprintf(3,"%s: Whoooooaaaaa!\n", ent->client->pers.netname); break;
case 12:gi_bprintf(3,"%s: Your sister!!\n", ent->client->pers.netname); break;
case 13:gi_bprintf(3,"%s: Your daughter!!!!\n", ent->client->pers.netname); break;
case 14:gi_bprintf(3,"%s: Your mama!\n", ent->client->pers.netname); break;
case 15:gi_bprintf(3,"%s: Arggggghhhh!\n", ent->client->pers.netname); break;
case 16:gi_bprintf(3,"%s: Your daddy!!!\n", ent->client->pers.netname); break;
case 17:gi_bprintf(3,"%s: Bite Me!\n", ent->client->pers.netname); break;
case 18:gi_bprintf(3,"%s: HeeeeHaaaaa\n", ent->client->pers.netname); }
ent->client->insulttime = level.time+60+(10*(rand()%6));
}
//==============================================
void TauntVictim(edict_t *ent, edict_t *victim) {
vec3_t vtmp;
if (!ent->isabot || ent == victim) return;
if (!victim || !victim->client) return;
if (ent->client->taunttime > level.time) return;
// Taunt only if near victim (don't reset timer)
VectorSubtract(ent->s.origin, victim->s.origin, vtmp);
if (VectorLength(vtmp) > 250) return;
switch (rand()%3) {
case 0:ent->s.frame = FRAME_flip01-1;
ent->client->anim_end = FRAME_flip12; break;
case 1:ent->s.frame = FRAME_salute01-1;
ent->client->anim_end = FRAME_salute11; break;
case 2:ent->s.frame = FRAME_taunt01-1;
ent->client->anim_end = FRAME_taunt17; }
ent->client->taunttime = level.time+30+(10*(rand()%6));
}
//==============================================
void RandomChat(edict_t *ent) {
if (ent->client->chattime > level.time) return;
if (ent->client->camptime > level.time) {
if (myrandom < 0.50) { // Camp and Chat about 50% of the time
if (ent->client->campitem == item_railgun)
switch (rand()%6) {
case 0: gi_bprintf(3,"%s: Bring firewood nexttime!!\n", ent->client->pers.netname); break;
case 1: gi_bprintf(3,"%s: Want a roasted weener?\n", ent->client->pers.netname); break;
case 2: gi_bprintf(3,"%s: Want a beer with that??\n", ent->client->pers.netname); break;
case 3: gi_bprintf(3,"%s: Don't ya just love it?\n", ent->client->pers.netname); break;
case 4: gi_bprintf(3,"%s: Get the camper at railgun!!\n", ent->client->pers.netname); break;
case 5: gi_bprintf(3,"%s: There's a camper at railgun!\n", ent->client->pers.netname); break; }
else
if (ent->client->campitem == item_health_mega)
switch (rand()%6) {
case 0: gi_bprintf(3,"%s: Kill the megahealth camper!!!\n", ent->client->pers.netname); break;
case 1: gi_bprintf(3,"%s: Want marshmellows?\n", ent->client->pers.netname); break;
case 2: gi_bprintf(3,"%s: Got hotdogs?\n", ent->client->pers.netname); break;
case 3: gi_bprintf(3,"%s: fuckin campers\n", ent->client->pers.netname); break;
case 4: gi_bprintf(3,"%s: Get the camper by megahealth!!\n", ent->client->pers.netname); break;
case 5: gi_bprintf(3,"%s: Camper at megahealth!\n", ent->client->pers.netname); break; }
ent->client->chattime = level.time+20;
return; } }
else
if (myrandom < 0.10) // Do this random chatting very rarely
switch (rand()%6) {
case 0: gi_bprintf(3,"%s: Bunch of Chicken Shits!\n", ent->client->pers.netname); break;
case 1: gi_bprintf(3,"%s: Come and get it!!!\n", ent->client->pers.netname); break;
case 2: gi_bprintf(3,"%s: Who wants a piece of me?\n", ent->client->pers.netname); break;
case 3: gi_bprintf(3,"%s: Where'd everybody go?\n", ent->client->pers.netname); break;
case 4: gi_bprintf(3,"%s: This server sucks!\n", ent->client->pers.netname); break;
case 5: gi_bprintf(3,"%s: Only pussies on this server!\n", ent->client->pers.netname); break; }
ent->client->chattime = level.time+60+(20*(rand()%6));
}
//=====================================================
//=========== BASIC TRACING ALGORITHMS ================
//=====================================================
//==============================================
qboolean InSight(edict_t *ent, edict_t *other) {
vec3_t start,end;
trace_t tr;
if (other->client && !G_ClientInGame(other))
return false;
VectorCopy(ent->s.origin,start);
start[2] += ent->viewheight-8;
VectorCopy(other->s.origin,end);
end[2] += other->viewheight-8;
if ((gi.pointcontents(start) & CONTENTS_WATER) && !other->waterlevel) {
tr = gi.trace(end, NULL, NULL, start, ent, CONTENTS_WINDOW|MASK_OPAQUE|CONTENTS_WATER);
if (tr.surface && tr.surface->flags & SURF_WARP) return false;
tr = gi.trace(start, NULL, NULL, end, ent, CONTENTS_WINDOW|MASK_OPAQUE);
return (tr.fraction == 1.0); }
if ((gi.pointcontents(start) & CONTENTS_WATER) && other->waterlevel) {
VectorCopy(other->s.origin,end);
end[2] -= 16;
tr = gi.trace(start, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_WINDOW);
return (tr.fraction == 1.0); }
if (other->waterlevel) {
VectorCopy(other->s.origin,end);
end[2] += 32;
tr = gi.trace(start, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_WATER);
if (tr.surface && tr.surface->flags & SURF_WARP) return false; }
return (gi.trace(start, NULL, NULL, end, ent, CONTENTS_WINDOW|MASK_OPAQUE).fraction == 1.0);
}
//==============================================
qboolean Bot_trace2(edict_t *ent,vec3_t ttz) {
vec3_t ttx;
VectorCopy(ent->s.origin,ttx);
ttx[2] += (ent->maxs[2]>=32)?24:-12;
return (gi.trace(ttx, NULL, NULL, ttz ,ent, MASK_OPAQUE).fraction == 1.0);
}
//==============================================
qboolean Bot_trace(edict_t *ent, edict_t *other) {
trace_t tr;
vec3_t ttx,tty;
VectorCopy(ent->s.origin,ttx);
VectorCopy(other->s.origin,tty);
if (ent->maxs[2] >=32) {
if (tty[2] > ttx[2])
tty[2] += 16;
ttx[2] += 30; }
else
ttx[2] -= 12;
tr = gi.trace(ttx,NULL,NULL,tty,ent,CONTENTS_WINDOW|MASK_OPAQUE);
if (tr.fraction == 1.0 && !tr.allsolid && !tr.startsolid) return true;
if (ent->maxs[2] < 32) return false;
if (tr.ent && tr.ent->use == door_use)
if (!tr.ent->targetname)
return true;
if (ent->s.origin[2] < other->s.origin[2] || ent->s.origin[2]-24 > other->s.origin[2])
return false;
ttx[2] -= 36;
tr = gi.trace(ttx, NULL, NULL, other->s.origin, ent, CONTENTS_WINDOW|MASK_OPAQUE);
return (tr.fraction == 1.0 && !tr.allsolid && !tr.startsolid);
}
//==================================================
qboolean TraceX(edict_t *ent,vec3_t p2) {
trace_t tr;
vec3_t v1,v2;
int contents;
contents = (CONTENTS_SOLID|CONTENTS_WINDOW);
if (ent->svflags & ~SVF_MONSTER) {
if (ent->client->waterstate) {
VectorCopy(ent->mins,v1);
VectorCopy(ent->maxs,v2); }
else
if (ent->client->ps.pmove.pm_flags & ~PMF_DUCKED) {
VectorSet(v1,-16,-16,-4);
VectorSet(v2,16,16,32); }
else {
VectorSet(v1,-4,-4,-4);
VectorSet(v2,4,4,4); } }
else {
VectorClear(v1);
VectorClear(v2);
contents |= (CONTENTS_LAVA|CONTENTS_SLIME); }
tr = gi.trace(ent->s.origin, v1, v2, p2, ent, contents);
if (tr.fraction == 1.0 && !tr.allsolid && !tr.startsolid)
return true;
if (ent->client->routetrace)
if (ent->svflags & SVF_MONSTER)
if (tr.ent && tr.ent->use == door_use)
return (tr.ent->moveinfo.state == 2);
return false;
}
//============================================================
void Get_RouteOrigin(int index,vec3_t pos) {
edict_t *e;
if (Route[index].state <= 3 || Route[index].state >= 20)
if (Route[index].state == 3) {
VectorCopy(Route[index].ent->s.origin,pos);
pos[2] += 8; }
else
VectorCopy(Route[index].Pt,pos);
switch (Route[index].state) {
case 4:
VectorCopy(Route[index].ent->union_ent->s.origin,pos);
pos[2] += 8;
return;
case 5:
if (!Route[index].ent->trainteam) {
VectorCopy(Route[index].ent->union_ent->s.origin,pos);
pos[2] += 8;
return; }
if (Route[index].ent->target_ent)
if (VectorCompare(Route[index].Tcourner, Route[index].ent->target_ent->s.origin)) {
VectorCopy(Route[index].ent->union_ent->s.origin,pos);
pos[2] += 8;
return; }
e = Route[index].ent->trainteam;
while (1) {
if (e == Route[index].ent)
break;
if (e->target_ent)
if (VectorCompare(Route[index].Tcourner, e->target_ent->s.origin)) {
VectorCopy(e->union_ent->s.origin,pos);
pos[2] += 8;
Route[index].ent = e;
return; }
e = e->trainteam; }
VectorCopy(Route[index].ent->union_ent->s.origin,pos);
pos[2] += 8;
return;
case 6:
if (Route[index].ent->union_ent) {
VectorCopy(Route[index].ent->union_ent->s.origin,pos);
pos[2] += 8; }
else
if (index+1 < TotalRouteNodes) {
if (Route[index+1].state <= 3) {
VectorCopy(Route[index+1].Pt,pos);
pos[2] += 8; }
else
if (Route[index+1].state <= 5) {
VectorCopy(Route[index+1].ent->union_ent->s.origin,pos);
pos[2] += 8; }
else
if (Route[index+1].state == 7) {
VectorCopy(Route[index+1].ent->union_ent->s.origin,pos);
pos[2] += 8; }
else
VectorCopy(Route[index+1].Pt,pos); }
else {
pos[0] = (Route[index].ent->absmin[0]+Route[index].ent->absmax[0])*0.5;
pos[1] = (Route[index].ent->absmin[1]+Route[index].ent->absmax[1])*0.5;
pos[2] = Route[index].ent->absmax[2]; }
return;
case 7:
VectorCopy(Route[index].ent->union_ent->s.origin,pos); }
}
//==============================================
void GetAimAngle(edict_t *ent, float aim, float dist) {
vec3_t targaim;
trace_t tr;
int weapon;
weapon = GetKindWeapon(ent->client->pers.weapon);
switch (weapon) {
case WEAP_BLASTER:
case WEAP_SHOTGUN:
case WEAP_SUPERSHOTGUN:
case WEAP_MACHINEGUN:
case WEAP_CHAINGUN:
case WEAP_HYPERBLASTER:
case WEAP_RAILGUN:
if (ent->client->current_enemy != ent->client->prev_enemy) {
if (ent->client->current_enemy->isabot)
VectorSubtract(ent->client->current_enemy->s.old_origin, ent->client->current_enemy->s.origin, targaim);
else {
VectorCopy(ent->client->current_enemy->velocity, targaim);
VectorInverse(targaim); }
VectorNormalize(targaim);
VectorMA(ent->client->current_enemy->s.origin, 5*aim*myrandom, targaim, targaim); }
else {
VectorSubtract(ent->client->targ_old_origin, ent->client->current_enemy->s.origin, targaim);
VectorMA(ent->client->current_enemy->s.origin, aim*myrandom, targaim, targaim); }
VectorSubtract(targaim, ent->s.origin, targaim);
AdjustAngle(ent, targaim, aim);
return;
case WEAP_GRENADES:
case WEAP_GRENADELAUNCHER:
case WEAP_ROCKETLAUNCHER:
if (ent->client->current_enemy != ent->client->prev_enemy) {
if (ent->client->current_enemy->isabot)
VectorSubtract(ent->client->current_enemy->s.origin, ent->client->current_enemy->s.old_origin, targaim);
else {
VectorCopy(ent->client->current_enemy->velocity, targaim);
VectorScale(targaim, 32, targaim); }
VectorNormalize(targaim);
VectorMA(ent->client->current_enemy->s.origin, (11-aim)*dist/25, targaim, targaim); }
else {
VectorSubtract(ent->client->current_enemy->s.origin, ent->client->targ_old_origin, targaim);
targaim[2] *= 0.5;
VectorMA(ent->client->current_enemy->s.origin, -aim*myrandom+dist/75, targaim, targaim); }
tr = gi.trace(ent->client->current_enemy->s.origin, NULL, NULL, targaim, ent->client->current_enemy, MASK_SHOT);
VectorCopy(tr.endpos, targaim);
if (weapon & WEAP_GRENADELAUNCHER|WEAP_ROCKETLAUNCHER) {
if (targaim[2] < (ent->s.origin[2]+JumpMax)) {
vec3_t vtmp;
targaim[2] -= 24;
VectorCopy(ent->s.origin, vtmp);
vtmp[2] += ent->viewheight-8;
tr = gi.trace(vtmp, NULL, NULL, targaim, ent, MASK_SHOT);
if (tr.fraction != 1.0)
targaim[2] += 24; }
else
if (targaim[2] >(ent->s.origin[2]+JumpMax))
targaim[2] += 5; }
VectorSubtract(targaim, ent->s.origin, targaim);
AdjustAngle(ent, targaim, aim);
return;
default: // BFG
VectorCopy(ent->client->vtemp, targaim);
VectorSubtract(targaim, ent->s.origin, targaim);
VectorSet(ent->s.angles,(Get_pitch(targaim)),(Get_yaw(targaim)),0.0F); }
}
//=================================================
qboolean HasAmmoForWeapon(edict_t *self, gitem_t *weapon) {
if (weapon==item_blaster) return true;
if ((int)dmflags->value & DF_INFINITE_AMMO) return true;
return (self->client->pers.inventory[ITEM_INDEX(FindItem(weapon->ammo))] >= weapon->quantity);
}
//========================================================
gitem_t *GetWeaponType(int weapnum) {
switch (weapnum) {
case WEAP_SHOTGUN: return item_shotgun;
case WEAP_SUPERSHOTGUN: return item_supershotgun;
case WEAP_MACHINEGUN: return item_machinegun;
case WEAP_CHAINGUN: return item_chaingun;
case WEAP_GRENADES: return item_grenades;
case WEAP_GRENADELAUNCHER: return item_grenadelauncher;
case WEAP_ROCKETLAUNCHER: return item_rocketlauncher;
case WEAP_HYPERBLASTER: return item_hyperblaster;
case WEAP_RAILGUN: return item_railgun;
case WEAP_BFG: return item_bfg; }
return item_blaster;
}
//==============================================
qboolean CanUseWeapon(edict_t *ent, int weapnum) {
gitem_t *weapon;
weapon = GetWeaponType(weapnum);
if (ent->client->pers.inventory[ITEM_INDEX(weapon)])
if (HasAmmoForWeapon(ent,weapon))
if (ent->client->weaponstate & WEAPON_READY|WEAPON_FIRING) {
if (ent->client->pers.weapon != weapon) {
ent->client->newweapon = weapon;
ChangeWeapon(ent); }
return true; } // true whether switched or not
return false;
}
//==============================================
qboolean Pickup_Navi(edict_t *ent, edict_t *other) {
int i;
if (!(ent->spawnflags & DROPPED_ITEM))
if (ent->item->quantity)
SetRespawn(ent, ent->item->quantity);
//on door (up & down)
if (ent->item == item_navi3 && ent->union_ent) {
int j,k;
qboolean flg=false;
if (ent->target_ent == other) {
other->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
other->client->waiting_obj = ent->union_ent;
if (ent->union_ent->spawnflags & 32) {
if (ent->union_ent->moveinfo.state & 3|1)
other->client->movestate |= 0x00000100;
else
other->client->movestate |= 0x00000080; }
else {
if (ent->union_ent->moveinfo.state & 3|0)
other->client->movestate |= 0x00000100;
else
if (ent->union_ent->moveinfo.state & 2|1)
other->client->movestate |= 0x00000080; }
for (i=-10;i<10;i++) {
if (i <= 0)
j = other->client->pers.routeindex-(10-i);
else
j = other->client->pers.routeindex+i;
if (j < 0) continue;
if (j >= TotalRouteNodes) continue;
if ((Route[j].state == 6 && Route[j].ent == ent->union_ent) || Route[j].state == 7) {
vec3_t v;
k = 1;
flg = false;
while (1) {
if ((j+k) >= TotalRouteNodes) break;
if ((j+k) >= other->client->pers.routeindex) {
Get_RouteOrigin(j+k,v);
if (fabs(v[2]-other->s.origin[2]) > JumpMax) {
flg = true;
break; } }
k++; }
if ((j+k) < TotalRouteNodes && flg) {
other->client->pers.routeindex = j+k;
break; } } }
if (!flg)
other->client->movestate |= 0x00000010;
ent->target_ent = NULL; }
SetRespawn(ent, 1000000);
ent->solid = SOLID_NOT; }
else
if (ent->item == item_navi2)
for (i=0;i<10;i++) {
if ((other->client->pers.routeindex+i) >= TotalRouteNodes) break;
if (!Route[other->client->pers.routeindex+i].index) break;
if (Route[other->client->pers.routeindex+i].state != 7) continue;
if (Route[other->client->pers.routeindex+i].ent == ent->union_ent) {
other->client->pers.routeindex += i+1;
break; } }
return true;
}
//==============================================
qboolean B_UseWeapon(edict_t *ent, float aim, float distance, int weap) {
if (!CanUseWeapon(ent,weap)) return false;
if (weap != WEAP_GRENADES && weap != WEAP_GRENADELAUNCHER)
if (!InSight(ent,ent->client->current_enemy))
return false;
GetAimAngle(ent,aim,distance);
ent->client->buttons |= BUTTON_ATTACK;
trace_priority = MaxOf(trace_priority,2);
return true;
}
//======================================================
//=========== BOT FIGHTING/COMBAT FUNCTIONS ============
//======================================================
//==============================================
void Combat_LevelX(edict_t *ent,float distance) {
vec3_t vdir;
if (ent->client->battlemode & 0x00000040) {
int k=0;
float aim = 10.0 - Bot[ent->client->pers.botindex].skill[AIMACCURACY];
//Rocket Launcher
if (distance > 200 && distance < 1000)
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_ROCKETLAUNCHER)
if (B_UseWeapon(ent,aim,distance,WEAP_ROCKETLAUNCHER))
k = 1;
//Grenade Launcher
if (!k && distance > 100 && distance < 400)
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_GRENADELAUNCHER)
if ((ent->client->current_enemy->s.origin[2]-ent->s.origin[2]) < 150)
if (B_UseWeapon(ent,aim,distance,WEAP_GRENADELAUNCHER))
k = 1;
//Hand Grenade
if (!k && distance > 200 && distance < 800)
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_GRENADES)
if (B_UseWeapon(ent,aim,distance,WEAP_GRENADES))
k = 1;
VectorSubtract(ent->client->vtemp,ent->s.origin,vdir);
ent->s.angles[YAW] = Get_yaw(vdir);
ent->s.angles[PITCH] = Get_pitch(vdir);
trace_priority = (k)?4:2;
return; }
VectorSubtract(ent->client->current_enemy->s.origin,ent->s.origin,vdir);
ent->s.angles[YAW] = Get_yaw(vdir);
ent->s.angles[PITCH] = Get_pitch(vdir);
trace_priority = 2; // Use this angle
}
qboolean Bot_Fall(edict_t *ent,vec3_t pos,float dist);
//==============================================
void Combat_Normal(edict_t *ent,float distance) {
edict_t *target;
int i,j,k;
vec3_t v,vv,v1,v2;
trace_t tr;
int enewep;
float f,aim;
target = ent->client->current_enemy;
aim = 10.0 - Bot[ent->client->pers.botindex].skill[AIMACCURACY];
// NOTE: skill = Bot[ent->client->pers.botindex].skill[COMBATSKILL];
//================================================
// Fire_Chicken -------------------------
if (ent->client->battlemode == 0x00000008)
aim *= 0.7;
//================================================
// Fire_Shift -------------------------
if (ent->client->battlemode & (0x00020000|0x00040000)) {
GetAimAngle(ent,aim,distance);
if (--ent->client->battlesubcnt > 0) {
if (ent->groundentity) {
if (ent->client->battlemode & 0x00020000) {
ent->client->moveyaw = ent->s.angles[YAW]+90;
if (ent->client->moveyaw > 180)
ent->client->moveyaw -= 360; }
else {
ent->client->moveyaw = ent->s.angles[YAW]-90;
if (ent->client->moveyaw < -180)
ent->client->moveyaw += 360; }
trace_priority = 3; } }
else
ent->client->battlemode &= ~(0x00020000|0x00040000); }
//================================================
// always try to dodge ---------------------------
if (ent->groundentity && !ent->waterlevel) {
AngleVectors(target->client->v_angle, v, NULL, NULL);
VectorScale(v,300,v);
VectorSet(vv,0,0,target->viewheight-8);
VectorAdd(target->s.origin,vv,vv);
VectorAdd(vv,v,v);
VectorSet(v1, -4, -4,-4);
VectorSet(v2, 4, 4, 4);
tr = gi.trace(vv,v1,v2,v,target,MASK_SHOT);
if (tr.ent == ent)
if ((tr.endpos[2] > (ent->s.origin[2]+4)) && (random() < 0.4)) { // BUG!
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
ent->client->battleduckcnt = 2+8*random(); }
else
if (tr.endpos[2] < (ent->s.origin[2]+JumpMax-24))
if (ent->client->routetrace) {
if (Bot_Fall(ent,ent->s.origin,0))
trace_priority = 3;; }
else {
ent->moveinfo.speed = 0.5;
ent->velocity[2] = 300;
ent->client->anim_priority = ANIM_JUMP; } }
//================================================
// Fire_Ignore -------------------------
if (ent->client->battlemode & 0x10000000) {
if (--ent->client->battlecount > 0)
if (ent->client->current_enemy == ent->client->prev_enemy)
return;
ent->client->battlemode = 0x00000000; }
//================================================
// Fire_PreStayFire -------------------------
if (ent->client->battlemode & 0x00000002) {
if (--ent->client->battlecount > 0) {
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
trace_priority = 4;
return; }
if (!(ent->client->battlemode & (0x00020000|0x00040000)))
ent->client->battlemode = 0x00000004;
ent->client->battlecount = 5+(int)(20*random()); }
//================================================
// Fire_StayFire -------------------------
if (ent->client->battlemode & 0x00000004) {
if (--ent->client->battlecount > 0) {
CanUseWeapon(ent,WEAP_BFG);
aim *= 0.95;
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
if (!(ent->client->battlemode & (0x00020000|0x00040000)))
trace_priority = 4;
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
return; }
ent->client->battlemode = 0x00000000; }
//================================================
if (ent->client->battlemode & 0x00000010) {
if (--ent->client->battlecount > 0) {
CanUseWeapon(ent,WEAP_BFG);
aim *= 0.95;
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING) {
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_BFG) {
if (target->s.origin[2] > ent->s.origin[2])
ent->client->ps.pmove.pm_flags |= PMF_DUCKED; }
else
ent->client->ps.pmove.pm_flags |= PMF_DUCKED; }
trace_priority = 3;
ent->client->moveyaw = ent->s.angles[YAW];
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
return; }
ent->client->battlemode = 0x00000000; }
//================================================
// Always avoid explosions -------------------------
if (--ent->client->battlecount > 0) {
CanUseWeapon(ent,WEAP_BFG);
aim *= 0.95;
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING) {
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_BFG) {
if (target->s.origin[2] > ent->s.origin[2])
ent->client->ps.pmove.pm_flags |= PMF_DUCKED; }
else
ent->client->ps.pmove.pm_flags |= PMF_DUCKED; }
trace_priority = 3;
ent->client->moveyaw = ent->s.angles[YAW]+180;
if (ent->client->moveyaw > 180)
ent->client->moveyaw -= 360;
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
return; }
//================================================
// Fire_BFG -------------------------
if (ent->client->battlemode & 0x00010000) {
if (--ent->client->battlecount > 0) {
CanUseWeapon(ent,WEAP_BFG);
aim *= 0.95;
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
trace_priority = 2;
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
return; }
ent->client->battlemode = 0x00000000; }
//================================================
// Fire_SeekRefuge -------------------------
if (ent->client->battlemode & 0x00001000) {
if (--ent->client->battlecount > 0) {
aim *= 0.95;
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING)
if (GetKindWeapon(ent->client->pers.weapon) == WEAP_BFG)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
trace_priority = 2;
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
return; }
ent->client->battlemode = 0x00000000;
ent->client->pers.routeindex -= 2; }
//================================================
if (ent->client->combatstate & ~0x00000001)
if (ent->client->movestate & (0x00000100|0x00000080|0x00000040|0x00000200))
if (abs(target->s.origin[2]-ent->s.origin[2]) < 300) {
// GrenadeLauncher
if (CanUseWeapon(ent,WEAP_GRENADELAUNCHER)) {
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if ((target->client->weaponstate == WEAPON_FIRING) || (ent->client->movestate & (0x00000100|0x00000080|0x00000040|0x00000200)))
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
ent->client->buttons |= BUTTON_ATTACK;
trace_priority = 2;
return; }
// Handgrenades
if (CanUseWeapon(ent,WEAP_GRENADES)) {
GetAimAngle(ent,aim,distance);
if (ent->groundentity)
if (target->client->weaponstate == WEAPON_FIRING)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
if (ent->client->weaponstate == WEAPON_READY)
ent->client->buttons |= BUTTON_ATTACK;
trace_priority = 2;
return; } }
//================================================
k=0;
if (ent->client->battlemode & ~(0x00020000|0x00040000))
if (Bot[ent->client->pers.botindex].skill[COMBATSKILL] > (random()*Bot[ent->client->pers.botindex].skill[COMBATSKILL]))
if ((30*random()) < Bot[ent->client->pers.botindex].skill[AGGRESSION]) {
enewep = GetKindWeapon(target->client->pers.weapon);
if (ent->client->routetrace && enewep != WEAP_RAILGUN) {
for (i=ent->client->pers.routeindex; i<(ent->client->pers.routeindex+10); i++) {
if (i >= TotalRouteNodes) break;
if (Route[i].state == 3)
if (Route[i].ent->solid == SOLID_TRIGGER) {
k = 1; break; } }
if (!k) {
GetAimAngle(ent,aim,distance);
f =target->s.angles[YAW]-ent->s.angles[YAW];
if (f > 180) f = -(360-f);
if (f < -180) f = -(f+360);
if (f <= -160) {
ent->client->battlemode |= 0x00040000; // strafe left
ent->client->battlesubcnt = 5+(int)(16*random()); }
else
if (f >= 160) {
ent->client->battlemode |= 0x00020000; // strafe right
ent->client->battlesubcnt = 5+(int)(16*random()); } } } }
//================================================
// Always avoid invincible enemy!
if (target->client->invincible_framenum > level.framenum) {
GetAimAngle(ent,aim,distance);
trace_priority = 3;
ent->client->moveyaw = ent->s.angles[YAW]+180; // Turn around!
if (ent->client->moveyaw > 180)
ent->client->moveyaw -= 360;
return; }
//================================================
// Always use the quad
if (ent->client->quad_framenum > level.framenum)
//SuperShotgun, HyperBlaster, Chaingun
if (CanUseWeapon(ent,WEAP_SUPERSHOTGUN)
|| (CanUseWeapon(ent,WEAP_HYPERBLASTER))
|| (CanUseWeapon(ent,WEAP_CHAINGUN))) {
GetAimAngle(ent,aim,distance);
if ((GetKindWeapon(ent->client->pers.weapon) & WEAP_BFG|WEAP_GRENADELAUNCHER) || InSight(ent,target))
ent->client->buttons |= BUTTON_ATTACK;
trace_priority = 2;
enewep = GetKindWeapon(target->client->pers.weapon);
if (enewep < WEAP_MACHINEGUN || enewep == WEAP_GRENADES) {
ent->client->battlemode |= 0x00000010;
ent->client->battlecount = 8+(int)(10*random()); }
return; }
//================================================
// Fire Refuge
if (SkillLevel[Bot[ent->client->pers.botindex].skill[COMBATSKILL]] & 0x00001000)
if (ent->client->battlemode == 0x00000000)
if (ent->client->routetrace && ent->client->pers.routeindex > 1) {
enewep = GetKindWeapon(target->client->pers.weapon);
j = (enewep >= WEAP_CHAINGUN && enewep != WEAP_GRENADES)?1:0;
Get_RouteOrigin(ent->client->pers.routeindex-2,v);
if (fabs(v[2]-ent->s.origin[2]) < JumpMax && j==1) {
if (GetKindWeapon(ent->client->pers.weapon) & WEAP_GRENADELAUNCHER|WEAP_ROCKETLAUNCHER|WEAP_BFG) {
ent->client->battlemode |= 0x00001000;
ent->client->battlecount = 8+(int)(10*random());
trace_priority = 4;
return; } } }
if (!ent->client->routetrace && distance < 100) {
ent->client->battlecount = 4+(int)(8*random());
trace_priority = 4; }
//BFG
if (distance > 400)
if (B_UseWeapon(ent,aim,distance,WEAP_BFG))
goto FIRED;
//Hyper Blaster
if (distance < 1200)
if (B_UseWeapon(ent,aim,distance,WEAP_HYPERBLASTER))
goto FIRED;
//Rocket
if ((distance > 100 && distance < 1200))
if (B_UseWeapon(ent,aim,distance,WEAP_ROCKETLAUNCHER))
goto FIRED;
//Railgun
if (distance < 1200)
if (B_UseWeapon(ent,aim,distance,WEAP_RAILGUN))
goto FIRED;
//Grenade Launcher
if (distance > 100 && distance < 400)
if ((target->s.origin[2]-ent->s.origin[2]) < 200)
if (B_UseWeapon(ent,aim,distance,WEAP_GRENADELAUNCHER))
goto FIRED;
//Chain Gun
if (distance < 1200)
if (B_UseWeapon(ent,aim,distance,WEAP_CHAINGUN))
goto FIRED;
//Machine Gun
if (distance < 600)
if (B_UseWeapon(ent,aim,distance,WEAP_MACHINEGUN))
goto FIRED;
//SuperShotgun
if (distance < 800)
if (B_UseWeapon(ent,aim,distance,WEAP_SUPERSHOTGUN))
goto FIRED;
// Should be firing?
if (ent->groundentity && distance >= 400)
if (SkillLevel[Bot[ent->client->pers.botindex].skill[COMBATSKILL]] & 0x10000000)
if (ent->client->movestate & ~(0x00000020|0x00000040|0x00000080|0x00000400|0x00000100|0x00000200)) {
ent->client->battlemode = 0x10000000;
ent->client->battlecount = 5+(int)(10*random()); }
//Shotgun
if (distance < 800)
if (B_UseWeapon(ent,aim,distance,WEAP_SHOTGUN))
goto FIRED;
//Hand Grenade
if (distance < 400)
if (B_UseWeapon(ent,aim,distance,WEAP_GRENADES))
goto FIRED;
//Blaster
if (B_UseWeapon(ent,aim,distance,WEAP_BLASTER))
goto FIRED;
return;
FIRED: // Shoot the weapon
if (ent->client->battlemode == 0x00000008) {
if (--ent->client->battlesubcnt > 0)
if (ent->groundentity && ent->waterlevel < 2) {
f =target->s.angles[YAW]-ent->s.angles[YAW];
if (f > 180) f = -(360-f);
if (f < -180) f = -(f+360);
if (fabs(f) >= 150)
ent->client->battlemode = 0x00000000;
else {
if (ent->client->weaponstate != WEAPON_READY)
if (target->s.origin[2] < ent->s.origin[2]) {
if (GetKindWeapon(ent->client->pers.weapon) & WEAP_ROCKETLAUNCHER|WEAP_GRENADELAUNCHER|WEAP_RAILGUN)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
else
if (Bot[ent->client->pers.botindex].skill[COMBATSKILL] >= 7) {
if (GetKindWeapon(ent->client->pers.weapon) & WEAP_SHOTGUN|WEAP_SUPERSHOTGUN|WEAP_BLASTER)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED; } }
trace_priority = 4; }
return; }
else
ent->client->battlemode = 0x00000000;
else
if (ent->client->battlemode == 0x00000000 && distance > 200)
if (ent->groundentity && ent->waterlevel < 2)
if (9*random() > Bot[ent->client->pers.botindex].skill[AGGRESSION]) {
if (GetKindWeapon(ent->client->pers.weapon) > WEAP_BLASTER && target->client->current_enemy != ent) {
f =target->s.angles[YAW]-ent->s.angles[YAW];
if (f > 180) f = -(360-f);
if (f < -180) f = -(f+360);
if (fabs(f) < 150) {
ent->client->battlemode = 0x00000008;
ent->client->battlesubcnt = 5+(int)(random()*8);
trace_priority = 4; } } } }
}
//============================================================
void Set_Combatstate(edict_t *ent) {
vec3_t vtmp;
float distance;
if (ent->client->movestate & 0x00000001) return;
if (!G_ClientInGame(ent->client->current_enemy)) {
ent->client->battleduckcnt = 0;
ent->client->current_enemy = NULL;
ent->client->combatstate &= ~0x00000001;
return; }
if (!Bot_trace(ent,ent->client->current_enemy)) {
if (ent->client->targetlock <= level.time) {
ent->client->current_enemy = NULL;
return; }
ent->client->combatstate |= 0x00000001; }
else {
ent->client->targetlock = level.time+1.2;
ent->client->combatstate &= ~0x00000001;
ent->client->battlemode &= ~0x00000040; }
VectorSubtract(ent->client->current_enemy->s.origin,ent->s.origin,vtmp);
distance = VectorLength(vtmp);
if (!(ent->client->combatstate & 0x00000001))
Combat_Normal(ent,distance);
else
if (ent->client->combatstate & 0x00001000)
Combat_Normal(ent,distance);
else
Combat_LevelX(ent,distance);
if (ent->client->current_enemy) {
ent->client->prev_enemy = ent->client->current_enemy;
VectorCopy(ent->client->current_enemy->s.origin, ent->client->targ_old_origin); }
}
//====================================================
//============ BOT ENEMY SEARCHING ROUTINES ==========
//====================================================
//====================================================
void Bot_SearchEnemy(edict_t *ent) {
qboolean tmpflg=false;
edict_t *target=NULL;
edict_t *trent;
int i,j;
vec3_t vdir;
if (ent->client->current_enemy)
if (Bot_trace(ent,ent->client->current_enemy))
tmpflg = true;
j = (random() < 0.5)?0:-1;
for (i=1; i <= maxclients->value && !target; i++) {
if (j)
trent = &g_edicts[i];
else
trent = &g_edicts[(int)(maxclients->value)-i+1];
if (!trent->inuse || ent == trent || trent->deadflag) continue;
if (ent->client->current_enemy == trent) continue;
if (ent->client->current_enemy && ent->client->current_enemy->health < 1) continue; // raven - added deadflag check
if (trent->movetype != MOVETYPE_NOCLIP) {
if (InSight(ent,trent)) {
VectorSubtract(trent->s.origin, ent->s.origin, vdir);
if (!tmpflg && !target) {
float vr = (float)Bot[ent->client->pers.botindex].skill[VRANGEVIEW];
float hr = (float)Bot[ent->client->pers.botindex].skill[HRANGEVIEW];
float pitch = fabs(Get_pitch(vdir)-ent->s.angles[PITCH]);
if (pitch > 180) pitch = 360-pitch;
if (pitch <= vr) {
float yaw = Get_yaw(vdir);
yaw = fabs(yaw-ent->s.angles[YAW]);
if (yaw > 180) yaw = 360-yaw;
if (yaw <= hr || (ent->client->movestate & (0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800)))
target = trent; } }
if (!tmpflg && !target && trent->mynoise && trent->mynoise2) {
if (trent->mynoise->teleport_time >= (level.time-FRAMETIME)) {
VectorSubtract(trent->mynoise->s.origin, ent->s.origin, vdir);
if (VectorLength(vdir) < 300) {
if ((9*random()) < 1+rand()%8)
target = trent; } }
if (!target && trent->mynoise2->teleport_time >= (level.time-FRAMETIME)) {
VectorSubtract(trent->mynoise->s.origin, ent->s.origin, vdir);
if (VectorLength(vdir) < 100) {
if ((9*random()) < 1+rand()%8)
target = trent; } } } }
else
if (!tmpflg && trent->mynoise)
if (trent->mynoise->teleport_time >= (level.time-FRAMETIME)) {
trace_t tr;
AngleVectors(trent->client->v_angle, vdir, NULL, NULL);
VectorScale(vdir,200,vdir);
VectorAdd(trent->s.origin,vdir,vdir);
tr = gi.trace(trent->s.origin,NULL,NULL,vdir,trent,MASK_SHOT);
VectorSubtract(ent->s.origin, tr.endpos, vdir);
if (VectorLength(vdir) < 500) {
VectorCopy(tr.endpos,vdir);
tr = gi.trace(ent->s.origin,NULL,NULL,vdir,ent,MASK_SHOT);
if (tr.fraction == 1.0 && (9*random()) < 1+rand()%8) {
target = trent;
ent->client->battlemode |= 0x00000040;
VectorCopy(vdir,ent->client->vtemp); } } } } }
if (target && !tmpflg)
ent->client->current_enemy = target;
else
if (target && ent->client->current_enemy)
if (GetKindWeapon(target->client->pers.weapon) > GetKindWeapon(ent->client->current_enemy->client->pers.weapon))
ent->client->current_enemy = target;
}
//==============================================
//============ ITEM HANDLING ROUTINES ==========
//==============================================
//==============================================
void InitAllItems(void) {
// set IT_ARMOR lookups
item_jacketarmor = FindItem("Jacket Armor");
item_combatarmor = FindItem("Combat Armor");
item_bodyarmor = FindItem("Body Armor");
item_armorshard = FindItem("Armor Shard");
item_powerscreen = FindItem("Power Screen");
item_powershield = FindItem("Power Shield");
// set IT_AMMO lookups
item_shells = FindItem("shells");
item_cells = FindItem("cells");
item_bullets = FindItem("bullets");
item_rockets = FindItem("rockets");
item_slugs = FindItem("slugs");
item_grenades = FindItem("grenades");
// set IT_WEAPON lookups
item_blaster = FindItem("blaster");
item_shotgun = FindItem("shotgun");
item_supershotgun = FindItem("super shotgun");
item_handgrenade = FindItem("grenades");
item_machinegun = FindItem("machinegun");
item_chaingun = FindItem("chaingun");
item_grenadelauncher = FindItem("grenade launcher");
item_rocketlauncher = FindItem("rocket launcher");
item_railgun = FindItem("railgun");
item_hyperblaster = FindItem("hyperblaster");
item_bfg = FindItem("bfg10k");
// set IT_HEALTH lookups
item_adrenaline = FindItem("Adrenaline");
item_health = FindItem("Health");
item_stimpak = FindItem("Health");
item_health_large = FindItem("Health");
item_health_mega = FindItem("Health");
// set IT_POWERUP lookups
item_quad = FindItem("Quad Damage");
item_invulnerability = FindItem("Invulnerability");
item_silencer = FindItem("Silencer");
item_breather = FindItem("Rebreather");
item_enviro = FindItem("Environment Suit");
// set IT_PACK lookups
item_pack = FindItem("Ammo Pack");
item_bandolier = FindItem("Bandolier");
// set IT_NODE lookups
item_navi1 = FindItem("Roam Navi1");
item_navi2 = FindItem("Roam Navi2");
item_navi3 = FindItem("Roam Navi3");
}
//==============================================
int GetKindWeapon(gitem_t *it) {
if (it->weaponthink == Weapon_Shotgun) return WEAP_SHOTGUN;
if (it->weaponthink == Weapon_SuperShotgun) return WEAP_SUPERSHOTGUN;
if (it->weaponthink == Weapon_Machinegun) return WEAP_MACHINEGUN;
if (it->weaponthink == Weapon_Chaingun) return WEAP_CHAINGUN;
if (it->weaponthink == Weapon_Grenade) return WEAP_GRENADES;
if (it->weaponthink == Weapon_GrenadeLauncher) return WEAP_GRENADELAUNCHER;
if (it->weaponthink == Weapon_RocketLauncher) return WEAP_ROCKETLAUNCHER;
if (it->weaponthink == Weapon_HyperBlaster) return WEAP_HYPERBLASTER;
if (it->weaponthink == Weapon_Railgun) return WEAP_RAILGUN;
if (it->weaponthink == Weapon_BFG) return WEAP_BFG;
return WEAP_BLASTER;
}
//====================================================
//============ BOT MOVEMENT TESTING ROUTINES =========
//====================================================
//============================================================
int Bot_TestMove(edict_t *ent,float ryaw,vec3_t pos,float dist,float *bottom) {
float yaw;
vec3_t trstart,trend;
vec3_t trmin,trmax,v,vv;
trace_t tr;
float tracelimit;
int contents;
if (ent->waterlevel >= 1)
tracelimit = 75;
else
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
tracelimit = 26;
else
tracelimit = JumpMax+5;
VectorSet(trmin,-16,-16,-24);
VectorSet(trmax,16,16,3);
if (ent->client->routetrace)
VectorSet(vv,16,16,0);
else
VectorSet(vv,16,16,3);
if (ent->client->routetrace)
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
if (ent->waterlevel < 2) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if ((v[2]-ent->s.origin[2]) > 20)
trmax[2] = 31; }
yaw = DEG2RAD(ryaw);
trend[0] = cos(yaw)*dist;
trend[1] = sin(yaw)*dist;
trend[2] = 0;
VectorAdd(trend, ent->s.origin, trstart);
VectorCopy(trstart,trend);
trend[2] += 1;
tr = gi.trace(trstart, trmin, trmax, trend, ent, MASK_BOTSOLIDX);
trmax[2] += 1;
if (tr.allsolid || tr.startsolid || tr.fraction != 1.0) {
float i;
qboolean moveok = false;
VectorCopy(trstart, trend);
for (i = 4; i <(tracelimit+4); i += 4) {
trstart[2] = ent->s.origin[2]+i;
tr = gi.trace(trstart, trmin, vv, trend, ent, MASK_BOTSOLIDX);
if (!tr.allsolid && !tr.startsolid && tr.fraction > 0) {
moveok = true;
break; } }
if (!moveok) return 0;
*bottom = tr.endpos[2]-ent->s.origin[2];
if (!ent->client->routetrace) {
if (tr.plane.normal[2] < 0.7 && (!ent->client->waterstate && ent->groundentity))
return 0; }
else {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (tr.plane.normal[2] < 0.7 && v[2] < ent->s.origin[2])
return 0; }
if (*bottom >tracelimit-5) return 0;
VectorCopy(tr.endpos,pos);
if (trmax[2] == 32) return 1;
VectorCopy(pos,trend);
trend[2] += 28;
tr = gi.trace(pos, trmin, trmax, trend, ent, MASK_BOTSOLIDX);
return (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0)?1:2; }
else {
VectorCopy(trstart,pos);
VectorCopy(trstart, trend);
trstart[2] = trend[2]-8190;
tr = gi.trace(trend, trmin, trmax, trstart, ent, MASK_BOTSOLIDX|MASK_OPAQUE);
*bottom = tr.endpos[2]-ent->s.origin[2];
contents = 0;
if (!ent->waterlevel) {
if (ent->client->enviro_framenum > level.framenum)
contents = CONTENTS_LAVA;
else
contents = (CONTENTS_LAVA|CONTENTS_SLIME); }
if (tr.contents & contents)
*bottom = -9999;
else
if (tr.surface->flags & SURF_SKY)
*bottom = -9999;
if (!ent->waterlevel && !ent->groundentity)
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
if (ent->velocity[2] > 10 && trmax[2] == 4)
return 2;
if (trmax[2] == 32) return 1;
VectorCopy(pos,trend);
trend[2] += 28;
tr = gi.trace(pos, trmin, trmax, trend, ent, MASK_BOTSOLIDX);
return (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0)?1:2; }
}
//============================================================
qboolean Bot_Watermove(edict_t *ent, vec3_t pos, float dist, float upd) {
trace_t tr;
vec3_t trmin,trmax,touchmin;
float i,j,max,vec;
VectorCopy(ent->s.origin,trmax);
trmax[2] += upd;
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, trmax, ent, MASK_BOTSOLIDX);
if (!tr.allsolid && !tr.startsolid && tr.fraction > 0) {
VectorCopy(tr.endpos,pos);
return true; }
VectorCopy(ent->s.origin,trmin);
trmin[2] += upd;
vec = -1;
max = 0;
for (i=0; i<360; i+=10) {
if (i && upd > -13 && upd < 0) break;
if (i > 60 && i < 300) continue;
j = ent->client->moveyaw+i;
if (j > 180) j -= 360;
else if (j < -180) j += 360;
else j = i;
touchmin[0] = cos(j)*24;
touchmin[1] = sin(j)*24;
touchmin[2] = 0;
VectorAdd(trmin,touchmin,trmax);
tr = gi.trace(trmax, ent->mins, ent->maxs, trmin, ent, MASK_BOTSOLIDX);
if (!tr.allsolid && !tr.startsolid) {
VectorAdd(tr.endpos,touchmin,trmax);
tr = gi.trace(trmax, ent->mins, ent->maxs, trmax, ent, MASK_BOTSOLIDX);
if (!tr.allsolid && !tr.startsolid) {
vec = i; break; } } }
if (vec == -1) return false;
VectorCopy(trmax,pos);
if (upd < 0)
ent->velocity[2] = 0;
return true;
}
//============================================================
qboolean Bot_moveW(edict_t *ent, float ryaw, vec3_t pos, float dist, float *bottom) {
float yaw;
vec3_t trstart,trend;
trace_t tr;
int contents;
if (ent->client->enviro_framenum > level.framenum)
contents = CONTENTS_LAVA;
else
contents = (CONTENTS_LAVA|CONTENTS_SLIME);
yaw = DEG2RAD(ryaw);
trend[0] = cos(yaw)*dist;
trend[1] = sin(yaw)*dist;
trend[2] = 0;
VectorAdd(trend, ent->s.origin, trstart);
VectorCopy(trstart,pos);
VectorCopy(trstart,trend);
trstart[2] = trend[2]-8190;
tr = gi.trace(trend, ent->mins, ent->maxs, trstart,ent, MASK_BOTSOLIDX|CONTENTS_WATER);
if ((trend[2]-tr.endpos[2]) >= 95) return false;
if (tr.contents & contents) return false;
if (!(tr.contents & CONTENTS_WATER)) return false;
*bottom = tr.endpos[2]-ent->s.origin[2];
return true;
}
//============================================================
qboolean Bot_Jump(edict_t *ent, vec3_t pos, float dist) {
float x,yaw,tdist,bottom,speed;
vec3_t temppos;
yaw = ent->client->moveyaw;
Bot_TestMove(ent,yaw,temppos,dist,&bottom);
if (bottom > -JumpMax) return false;
for (x=2; x<=16; x++) {
tdist = dist*x;
if (Bot_TestMove(ent,yaw,temppos,tdist,&bottom)) {
if (bottom <= JumpMax && bottom > -JumpMax)
if (Get_FlyingSpeed(bottom,x,dist,&speed)) {
speed *= 1.5;
if (speed > 1.2) speed = 1.2;
ent->moveinfo.speed = speed;
ent->velocity[2] = 300;
SetBotAnim(ent);
return true; }
continue; }
else
return false; }
return false;
}
//============================================================
qboolean Bot_Fall(edict_t *ent,vec3_t pos,float dist) {
float x,n,speed,vel,yori,ypos;
vec3_t vdir,vv;
int mf = 0;
int mode = 0;
if (ent->client->routetrace) {
mode = 2;
Get_RouteOrigin(ent->client->pers.routeindex,vv);
ypos = vv[2];
if (!HazardCheck(ent,vv)) {
if (++ent->client->pers.routeindex >= TotalRouteNodes)
ent->client->pers.routeindex = 0;
return false; }
yori = pos[2];
VectorSubtract(vv,pos,vdir);
if (vdir[2] >= 0) goto JUMPCATCH;
vel = ent->velocity[2];
n = 1.0;
for (x=1; x<=30; ++x,n+=x) {
vel -= (ent->gravity*sv_gravity->value*FRAMETIME);
yori += vel*0.1;
if (ypos >= yori) {
mf = 1;
break; } }
VectorCopy(vdir,vv);
vv[2] = 0;
if (Route[ent->client->pers.routeindex].state == 5) {
vv[0] += 0.1*Route[ent->client->pers.routeindex].ent->velocity[0]*x;
vv[1] += 0.1*Route[ent->client->pers.routeindex].ent->velocity[1]*x; }
speed = VectorLength(vv)/x;
if (speed <= 30 && mf) {
ent->moveinfo.speed = speed/30;
VectorCopy(pos,ent->s.origin);
return true; }
goto JUMPCATCH; }
goto JMPCHK;
JUMPCATCH:
vel = 300;
yori = pos[2];
mf = 0;
for (x=1; x<=30; ++x) {
vel -= (ent->gravity*sv_gravity->value*FRAMETIME);
yori += vel*0.1;
if (vel > 0) {
if (mf == 0) {
if (ypos < yori)
mf = 2; } }
else if (x > 1) {
if (mf == 0) {
if (ypos < yori)
mf = 2; }
else if (mf == 2) {
if (ypos >= yori) {
mf = 1;
break; } } } }
VectorCopy(vdir,vv);
vv[2] = 0;
if (mode == 2)
if (Route[ent->client->pers.routeindex].state == 5) {
vv[0] += 0.1*Route[ent->client->pers.routeindex].ent->velocity[0]*x;
vv[1] += 0.1*Route[ent->client->pers.routeindex].ent->velocity[1]*x; }
n = VectorLength(vv);
if (x > 1) n /= (x-1);
if (n < 30 && mf) {
ent->moveinfo.speed = n/30;
VectorCopy(pos,ent->s.origin);
ent->velocity[2] = 300;
SetBotAnim(ent);
return true; }
JMPCHK:
if (Bot_Jump(ent,pos,dist)) return true;
return false;
}
//============================================================
qboolean TargetJump(edict_t *ent,vec3_t tpos) {
float x,n,jvel,vel,yori;
vec3_t vdir,vv;
int mf=0;
jvel = vel = 300;
yori = ent->s.origin[2];
if (!HazardCheck(ent,tpos)) return false;
VectorSubtract(tpos,ent->s.origin,vdir);
for (x=1; x<=60; ++x) {
vel -= (ent->gravity*sv_gravity->value*0.1);
yori += vel*0.1;
if (vel > 0) {
if (mf == 0) {
if (tpos[2] < yori)
mf = 2; } }
else if (x > 1) {
if (mf == 0) {
if (tpos[2] < yori)
mf = 2; }
else if (mf == 2) {
if (tpos[2] >= yori) {
mf = 1;
break; } } } }
VectorCopy(vdir,vv);
vv[2] = 0;
n = VectorLength(vv);
if (x > 1) n /= (x-1);
if (n < 30 && mf) {
ent->moveinfo.speed = n/30;
ent->velocity[2] = jvel;
SetBotAnim(ent);
return true; }
return false;
}
//============================================================
qboolean TargetJump_Chk(edict_t *ent,vec3_t tpos,float defvel) {
float x,n,vel,yori;
vec3_t vdir,vv;
int mf = 0;
vel = defvel+300;
yori = ent->s.origin[2];
if (!HazardCheck(ent,tpos)) return false;
VectorSubtract(tpos,ent->s.origin,vdir);
for (x=1; x<=60; ++x) {
vel -= (ent->gravity*sv_gravity->value*0.1);
yori += vel*0.1;
if (vel > 0) {
if (mf == 0) {
if (tpos[2] < yori)
mf = 2; } }
else if (x > 1) {
if (mf == 0) {
if (tpos[2] < yori)
mf = 2; }
else if (mf == 2) {
if (tpos[2] >= yori) {
mf = 1;
break; } } } }
VectorCopy(vdir,vv);
vv[2] = 0;
n = VectorLength(vv);
if (x > 1) n /= (x-1);
return (n<30 && mf!=0);
}
//============================================================
void Get_WaterState(edict_t *ent) {
ent->watertype = gi.pointcontents(ent->s.origin);
ent->waterlevel = (ent->watertype & MASK_WATER)?1:0;
if (ent->waterlevel) {
float x;
trace_t tr;
vec3_t trmin,trmax;
VectorCopy(ent->s.origin,trmax);
VectorCopy(ent->s.origin,trmin);
trmax[2] -= 24;
trmin[2] += 8;
tr = gi.trace(trmin, NULL, NULL, trmax, ent, MASK_WATER);
x = trmin[2]-tr.endpos[2];
if (tr.allsolid || tr.startsolid || (x < 4.0))
ent->client->waterstate = 2;
else
ent->client->waterstate = (x >= 4.0 && x <= 12.0)?1:0; }
else
ent->client->waterstate = 0;
}
//============================================================
void Search_NearbyPod(edict_t *ent) {
if (Route[ent->client->pers.routeindex].state >= 3) return;
if ((ent->client->pers.routeindex+1) < TotalRouteNodes)
if (Route[ent->client->pers.routeindex+1].state < 3) {
vec3_t v;
Get_RouteOrigin(ent->client->pers.routeindex+1,v);
if (TraceX(ent,v)) {
vec3_t v1,v2;
float x;
VectorSubtract(v,ent->s.origin,v1);
Get_RouteOrigin(ent->client->pers.routeindex,v);
VectorSubtract(v,ent->s.origin,v2);
x = fabs(v1[2]);
if (VectorLength(v1) < VectorLength(v2) && x <= JumpMax && Route[ent->client->pers.routeindex].state <= 1)
ent->client->pers.routeindex++;
else {
if (ent->client->waterstate==0)
if (v2[2] > JumpMax)
if (fabs(v1[2]) < JumpMax)
ent->client->pers.routeindex++; } } }
}
//========================================================
void BotAI(edict_t *ent) {
float dist,x,yaw,iyaw,f1,f2,f3,bottom;
int tempflag,i,j,k;
edict_t *it_ent,*touch[1024],*trent;
vec3_t touchmin,touchmax,v,vv;
vec3_t temppos,trmin,trmax;
qboolean ladderdrop,canrocj,waterjumped;
gitem_t *it;
edict_t *front,*left,*right,*e;
char *str;
cplane_t plane;
trace_t tr;
myrandom=random();
if (ent->client->changetime < level.time) {
RandomizeParameters(ent->client->pers.botindex);
ent->client->changetime = level.time+30+10*(int)rand()%9; }
trace_priority = 1;
ent->client->objshot = false;
ent->client->buttons &= ~BUTTON_ATTACK;
if (VectorCompare(ent->s.origin,ent->s.old_origin))
if (!ent->groundentity && !ent->waterlevel) {
VectorCopy(ent->s.origin,v);
v[2] -= 1.0;
tr = gi.trace(ent->s.origin,ent->mins,ent->maxs,v,ent,MASK_BOTSOLIDX);
if (!tr.allsolid && !tr.startsolid)
ent->groundentity = tr.ent; }
if (JumpMax == 0) {
x = 300-ent->gravity*sv_gravity->value*0.1;
JumpMax = 0;
while (1) {
JumpMax += x*0.1;
x -= ent->gravity*sv_gravity->value*0.1;
if (x < 0) break; } }
if (!ent->client->havetarget && ent->client->routetrace) {
j = Bot[ent->client->pers.botindex].skill[PRIMARYWEAP];
if (j && !ent->client->pers.inventory[j]) {
it = &itemlist[j];
if (ent->client->enemy_routeindex < ent->client->pers.routeindex || ent->client->enemy_routeindex >= TotalRouteNodes)
ent->client->enemy_routeindex = ent->client->pers.routeindex;
for (i = ent->client->enemy_routeindex+1;i<(ent->client->enemy_routeindex+50);i++) {
if (i > TotalRouteNodes) break;
if (Route[i].state == 3) {
if (Route[i].ent->item == it) {
ent->client->havetarget = true;
break; }
else
if (Route[i].ent->solid == SOLID_TRIGGER) {
if (Route[i].ent->item == &itemlist[j]) {
ent->client->havetarget = true;
break; } } } }
ent->client->enemy_routeindex = i; }
else
if (j=ITEM_INDEX(item_quad)) {
it = &itemlist[j];
if (ent->client->enemy_routeindex < ent->client->pers.routeindex || ent->client->enemy_routeindex >= TotalRouteNodes)
ent->client->enemy_routeindex = ent->client->pers.routeindex;
for (i = ent->client->enemy_routeindex+1;i<(ent->client->enemy_routeindex+25);i++) {
if (i > TotalRouteNodes) break;
if (Route[i].state == 3) {
if (Route[i].ent->item == it) {
if (Route[i].ent->solid == SOLID_TRIGGER) {
ent->client->havetarget = true;
break; } } } }
ent->client->enemy_routeindex = i; } }
else
if (ent->client->havetarget)
if (ent->client->enemy_routeindex < ent->client->pers.routeindex) {
ent->client->havetarget = false;
ent->client->enemy_routeindex = ent->client->pers.routeindex; }
canrocj = (ent->client->pers.inventory[ITEM_INDEX(item_rocketlauncher)] && ent->client->pers.inventory[ITEM_INDEX(item_rockets)] > 0);
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) {
if (ent->client->battleduckcnt > 0)
if (ent->groundentity)
goto DCHCANC;
VectorSet(v,16,16,32);
VectorCopy(ent->s.origin,v);
v[2] += 28;
tr = gi.trace(ent->s.origin,ent->mins,ent->maxs,v,ent,MASK_BOTSOLIDX);
if (!tr.startsolid && !tr.allsolid && tr.fraction == 1.0) {
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
ent->maxs[2] = 32; } }
else
if (ent->velocity[2] > 10 && !ent->groundentity)
if (!(ent->client->movestate & (0x00000008|0x00000002|0x00000004))) {
VectorSet(v,16,16,40);
tr = gi.trace(ent->s.origin,ent->mins,v,ent->s.origin,ent,MASK_BOTSOLIDX);
if (tr.startsolid || tr.allsolid) {
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
ent->maxs[2] = 4; } }
DCHCANC:
if (ent->groundentity || ent->waterlevel) {
if (ent->waterlevel) {
if (!(ent->client->movestate & 0x00000004))
ent->client->movestate &= ~(0x00000008|0x00000002|0x00000004); }
else
ent->client->movestate &= ~(0x00000008|0x00000002|0x00000004);
if (ent->groundentity && !ent->waterlevel)
ent->moveinfo.speed = 1.0;
else
if (ent->waterlevel && ent->velocity[2] <= 1)
ent->moveinfo.speed = 1.0; }
if ((ent->client->ps.pmove.pm_flags & PMF_DUCKED) && ent->groundentity)
dist = 10*ent->moveinfo.speed;
else {
dist = 30*ent->moveinfo.speed;
if (ent->groundentity)
dist *= ent->client->ground_slope; }
Get_WaterState(ent);
ent->client->enemysearchcnt += 2;
if (ent->client->enemysearchcnt >= 10) {
Bot_SearchEnemy(ent);
ent->client->enemysearchcnt = 1+rand()%8;
if (ent->client->enemysearchcnt > 10)
ent->client->enemysearchcnt = 10;
if (ent->client->enemysearchcnt < 0)
ent->client->enemysearchcnt = 0; }
Set_Combatstate(ent);
if (trace_priority == 4)
goto VCHCANSEL;
if (ent->client->routetrace) {
if (Route[ent->client->pers.routeindex].state >= 0)
Search_NearbyPod(ent);
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (ent->client->movestate & (0x00000020|0x00000040|0x00000080|0x00000400|0x00000100|0x00000200))
ent->client->routelocktime = level.time+1.5;
else
if (Route[ent->client->pers.routeindex].state <= 3 && ((v[2]-ent->s.origin[2]) > JumpMax && !ent->client->waterstate) && !(ent->client->movestate & 0x00000001)) {
if (ent->client->routelocktime <= level.time) {
ent->client->routetrace = false;
ent->client->routereleasetime = level.time+2.0; } }
else
if (!TraceX(ent,v)) {
k=0;
if (ent->groundentity) {
if (ent->groundentity->use == train_use) {
ent->client->routelocktime = level.time+1.5;
k = 1; } }
if (ent->client->routelocktime <= level.time && !k) {
ent->client->routetrace = false;
ent->client->routereleasetime = level.time+2.0; } }
else
ent->client->routelocktime = level.time+1.5; }
if (trace_priority == 4)
goto VCHCANSEL;
if (ent->client->movestate & 0x00000001) {
ent->velocity[2] = 200;
VectorCopy(ent->mins,trmin);
trmin[2] += 20;
yaw = DEG2RAD(ent->client->moveyaw);
touchmin[0] = cos(yaw)*32;
touchmin[1] = sin(yaw)*32;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,touchmax);
tr = gi.trace(ent->s.origin, trmin,ent->maxs, touchmax,ent, MASK_BOTSOLID);
plane = tr.plane;
if (!(tr.contents & CONTENTS_LADDER) && !tr.allsolid) {
if (ent->velocity[2] <= 200)
if (!ent->waterlevel)
ent->velocity[2] = 200;
ent->client->movestate &= ~0x00000001;
ent->moveinfo.speed = 0.25;
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (VectorLength(v) > 32) {
VectorSubtract(v,ent->s.origin,v);
ent->client->moveyaw = Get_yaw(v);
if (trace_priority < 2)
ent->s.angles[YAW] = ent->client->moveyaw; }
else
ent->client->pers.routeindex++; } }
else {
if (!tr.allsolid)
VectorCopy(tr.endpos,ent->s.origin);
VectorCopy(ent->s.origin,touchmin);
touchmin[2] += 8;
tr = gi.trace(ent->s.origin, ent->mins,ent->maxs, touchmin,ent, MASK_BOTSOLID);
x = tr.endpos[2]-ent->s.origin[2];
ent->s.origin[2] += x;
e = tr.ent;
if (x == 0) {
x = Get_yaw(plane.normal);
VectorCopy(ent->s.origin,v);
yaw = x+90;
if (yaw > 180) yaw -= 360;
yaw = DEG2RAD(yaw);
touchmin[0] = cos(yaw)*48;
touchmin[1] = sin(yaw)*48;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,trmin);
VectorCopy(trmin,trmax);
trmin[2] += 32;
trmax[2] += 64;
tr = gi.trace(trmin,NULL,NULL,trmax,ent,MASK_BOTSOLID);
f1 = tr.fraction;
VectorCopy(ent->s.origin,v);
iyaw = x -90;
if (iyaw < 180) iyaw += 360;
iyaw = DEG2RAD(iyaw);
touchmin[0] = cos(iyaw)*48;
touchmin[1] = sin(iyaw)*48;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,trmin);
VectorCopy(trmin,trmax);
trmin[2] += 32;
trmax[2] += 64;
tr = gi.trace(trmin,NULL,NULL,trmax,ent,MASK_BOTSOLID);
f2 = tr.fraction;
x = 0.0;
if (f1 == 1.0 && f2 != 1.0)
x = yaw;
else
if (f1 != 1.0 && f2 == 1.0)
x = iyaw;
if (x != 0.0) {
touchmin[0] = cos(x)*4;
touchmin[1] = sin(x)*4;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,trmin);
tr = gi.trace(ent->s.origin,ent->mins,ent->maxs,trmin,ent,MASK_BOTSOLID);
if (tr.startsolid || tr.allsolid)
x = 0;
else
VectorCopy(tr.endpos,ent->s.origin); }
if (x == 0.0) {
k = 0;
if (e) {
if (e->use == door_use) {
if (e->moveinfo.state == 2)
k = 1; } }
if (!k) {
ent->client->moveyaw += 180;
if (ent->client->moveyaw > 180)
ent->client->moveyaw -= 360;
ent->client->movestate &= ~0x00000001;
ent->moveinfo.speed = 0.25; } } } }
if (ent->client->movestate & 0x00000001) {
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (v[2] < ent->s.origin[2]) {
VectorSubtract(ent->s.origin,v,vv);
vv[2] = 0;
if (VectorLength(vv) < 32)
ent->client->pers.routeindex++; } }
ent->velocity[0] = 0;
ent->velocity[1] = 0;
goto VCHCANSEL_L; } }
if (ent->groundentity && ent->waterlevel <= 1)
if (trace_priority < 2)
ent->s.angles[PITCH] = 0;
if (ent->groundentity && !ent->client->routetrace) {
if (trace_priority < 3)
ent->client->moveyaw = ent->s.angles[YAW]; }
else
if (trace_priority < 2)
ent->s.angles[YAW] = ent->client->moveyaw;
if (!ent->client->routetrace && ent->client->routereleasetime <= level.time) {
if (ent->client->pers.routeindex >= TotalRouteNodes)
ent->client->pers.routeindex = 0;
for (i=0; i<TotalRouteNodes && i<12;i++) {
if (Route[ent->client->pers.routeindex].state == 21) {
while (1) {
++ent->client->pers.routeindex;
if (ent->client->pers.routeindex >= TotalRouteNodes) {
i = TotalRouteNodes;
break; }
if (Route[ent->client->pers.routeindex].state == 22) {
++ent->client->pers.routeindex;
break; } }
continue; }
else
if (Route[ent->client->pers.routeindex].state == 22) {
++ent->client->pers.routeindex; continue; }
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (Route[ent->client->pers.routeindex].state <= 3 && TraceX(ent,v)) {
if (fabs(v[2]-ent->s.origin[2]) <= JumpMax || ent->client->waterstate == 2) {
ent->client->routetrace = true;
ent->client->routelocktime = level.time+1.5;
break; } }
if (++ent->client->pers.routeindex >= TotalRouteNodes)
ent->client->pers.routeindex = 0; } }
else
if (ent->client->routetrace) {
if (Route[ent->client->pers.routeindex].state == 6) {
it_ent = Route[ent->client->pers.routeindex].ent;
if (ent->client->pers.routeindex+1 < TotalRouteNodes) {
Get_RouteOrigin(ent->client->pers.routeindex+1,v);
ent->client->routetrace = false;
j = TraceX(ent,v);
ent->client->routetrace = true;
if ((!j ||(v[2]-ent->s.origin[2]) > JumpMax)&& it_ent->union_ent) {
k = ((it_ent->union_ent->s.origin[2]-ent->s.origin[2]) > JumpMax)?1:0;
VectorSubtract(it_ent->union_ent->s.origin,ent->s.origin,temppos);
yaw = Get_yaw(temppos);
if (trace_priority < 2) {
ent->s.angles[PITCH] = Get_pitch(temppos);
ent->s.angles[YAW] = yaw; }
temppos[2] = 0;
x = VectorLength(temppos);
if (x == 0 || k) {
if (it_ent->nextthink >= level.time)
ent->client->routelocktime = level.time+1.5;
goto VCHCANSEL; }
if (x < dist)
dist = x;
if (it_ent->nextthink > level.time)
ent->client->routelocktime = it_ent->nextthink+1.5;
else
ent->client->routelocktime = level.time+1.5;
if (trace_priority < 3)
ent->client->moveyaw = yaw;
goto GOMOVE; } }
ent->client->pers.routeindex++; }
if (ent->client->pers.routeindex < TotalRouteNodes) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
k = 0;
if (Route[ent->client->pers.routeindex].state == 7) {
it_ent = Route[ent->client->pers.routeindex].ent;
if (it_ent->health && (it_ent->takedamage || it_ent->moveinfo.state != 0))
k = 2;
else
if (it_ent->health) {
ent->client->pers.routeindex++;
if (ent->client->pers.routeindex < TotalRouteNodes)
Get_RouteOrigin(ent->client->pers.routeindex,v); } }
else {
VectorSet(touchmax,16,16,4);
VectorSet(touchmin,-16,-16,0);
tr = gi.trace(ent->s.origin,touchmin,touchmax,v,ent,MASK_SHOT);
if (tr.fraction != 1.0 && tr.ent) {
if (tr.ent->health || tr.ent->takedamage)
if (tr.ent->classname[0] != 'p' && tr.ent->classname[0] != 'b') {
ent->client->routelocktime = level.time+1.5;
it_ent = tr.ent;
k = 1; } } }
if (k && !(ent->client->buttons & BUTTON_ATTACK)) {
trmin[0] = (it_ent->absmin[0]+it_ent->absmax[0])*0.5;
trmin[1] = (it_ent->absmin[1]+it_ent->absmax[1])*0.5;
trmin[2] = (it_ent->absmin[2]+it_ent->absmax[2])*0.5;
if (k == 2) {
VectorSet(touchmin, 0, 0, ent->viewheight-8);
VectorAdd(ent->s.origin,touchmin,touchmin);
tr = gi.trace(it_ent->union_ent->s.origin,NULL,NULL,trmin,it_ent->union_ent,MASK_SHOT);
VectorSubtract(tr.endpos,ent->s.origin,trmax); }
else
VectorSubtract(v,ent->s.origin,trmax);
if (!ent->client->current_enemy && it_ent->takedamage) {
ent->client->newweapon = item_blaster;
ChangeWeapon(ent);
ent->client->pers.weapon->use(ent,item_blaster); }
if (!ent->client->current_enemy || it_ent->takedamage) {
ent->s.angles[YAW] = Get_yaw(trmax);
ent->s.angles[PITCH] = Get_pitch(trmax); }
if (it_ent->takedamage)
ent->client->buttons |= BUTTON_ATTACK;
if (k == 2) {
if (it_ent->moveinfo.state != 0)
goto VCHCANSEL; }
else {
if (!TraceX(ent,v))
goto VCHCANSEL; } }
if (Route[ent->client->pers.routeindex].state == 5 && !ent->client->waterstate) {
Get_RouteOrigin(ent->client->pers.routeindex -1 ,trmin);
if ((trmin[2]-ent->s.origin[2]) > JumpMax && (v[2]-ent->s.origin[2]) > JumpMax && ent->waterlevel < 3) {
ent->client->routetrace = false; } }
f2=(ent->client->waterstate == 2)?20:(ent->groundentity)?-8:0;
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
f1 = -16;
else {
if (ent->client->waterstate == 2)
f1 = 24;
else
if (ent->waterlevel && ent->waterlevel < 3) {
f1 = ((v[0] == ent->s.origin[0] && v[1] == ent->s.origin[1])?-300:(-(JumpMax+64))); }
else
f1 = -(JumpMax+64); }
yaw = (Route[ent->client->pers.routeindex].state == 1)?-48:12;
if (v[0] <= (ent->absmax[0]-yaw) && v[0] >= (ent->absmin[0]+yaw)) {
if (v[1] <= (ent->absmax[1]-yaw) && v[1] >= (ent->absmin[1]+yaw)) {
if ((v[2] <= (ent->absmax[2]-f1) && v[2] >= (ent->absmin[2]+f2))
|| Route[ent->client->pers.routeindex].state == 1) {
if (ent->client->pers.routeindex < TotalRouteNodes) {
if (Route[ent->client->pers.routeindex].state <= 3) {
if (ent->client->havetarget) {
for (i = 0;i <(6);i++) {
if (!(k=Route[ent->client->pers.routeindex].linkpod[i])) break;
if (k > ent->client->pers.routeindex && k < ent->client->enemy_routeindex) {
ent->client->pers.routeindex = k;
break; } } }
else
if (random() < 0.2) {
for (i = 0;i <(6);i++) {
if (!(k=Route[ent->client->pers.routeindex].linkpod[i])) break;
if (k > ent->client->pers.routeindex && k < ent->client->enemy_routeindex) {
if (random() < 0.5) {
ent->client->pers.routeindex = k;
break; } } } } }
ent->client->pers.routeindex++;
if (!(ent->client->pers.routeindex < TotalRouteNodes))
ent->client->pers.routeindex = 0; } } } }
if (ent->client->pers.routeindex < TotalRouteNodes && trace_priority) {
if (1) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
VectorSubtract(v,ent->s.origin,temppos);
if (trace_priority < 2)
ent->s.angles[PITCH] = Get_pitch(temppos);
k = 0;
if (ent->groundentity || ent->waterlevel) {
yaw = temppos[2];
temppos[2] = 0;
x = VectorLength(temppos);
if (1) {
k = 0;
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos);
if ((ent->groundentity || ent->waterlevel) && trace_priority < 2) {
ent->s.angles[YAW] = ent->client->moveyaw;
k = 1; }
if (x < dist && fabs(yaw) < 20 && k) {
iyaw = Get_yaw(temppos);
i = Bot_TestMove(ent,iyaw,temppos,x,&bottom);
tr = gi.trace(v,ent->mins,ent->maxs,v,ent,MASK_BOTSOLIDX);
if (Route[ent->client->pers.routeindex].state == 3 && !i) {
if (x < 30) ent->client->pers.routeindex++; }
else
if ((Route[ent->client->pers.routeindex].state == 3
|| Route[ent->client->pers.routeindex].state == 0)
&& !tr.allsolid && !tr.startsolid
&& HazardCheck(ent,v)
&& fabs(bottom) < 20 && i && !ent->waterlevel) {
if ((v[2] < ent->s.origin[2] && bottom < 0) || (v[2] >= ent->s.origin[2] && bottom >= 0)) {
VectorCopy(temppos,ent->s.origin);
VectorCopy(v,trmin);
dist -= x;
if (Route[ent->client->pers.routeindex].state <= 3) {
if (ent->client->havetarget) {
for (i = 0;i <(6);i++) {
if (!(j=Route[ent->client->pers.routeindex].linkpod[i])) break;
if (j > ent->client->pers.routeindex && j < ent->client->enemy_routeindex) {
ent->client->pers.routeindex = j;
break; } } } }
ent->client->pers.routeindex++;
if (i == 2)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
Get_RouteOrigin(ent->client->pers.routeindex,v);
VectorSubtract(v,ent->s.origin,temppos);
if (trace_priority < 2)
ent->s.angles[PITCH] = Get_pitch(temppos);
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos);
if (k && trace_priority < 2)
ent->s.angles[YAW] = ent->client->moveyaw; } }
else
if ((Route[ent->client->pers.routeindex].state == 3
|| Route[ent->client->pers.routeindex].state == 0)
&& fabs(bottom) < 20 && ent->waterlevel) {
if ((v[2] < ent->s.origin[2] && bottom < 0) || (v[2] >= ent->s.origin[2] && bottom >= 0)) {
VectorCopy(temppos,ent->s.origin);
VectorCopy(v,trmin);
dist -= x;
ent->client->pers.routeindex++;
Get_RouteOrigin(ent->client->pers.routeindex,v);
VectorSubtract(v,ent->s.origin,temppos);
if (trace_priority < 2)
ent->s.angles[PITCH] = Get_pitch(temppos);
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos);
if (k && trace_priority < 2)
ent->s.angles[YAW] = ent->client->moveyaw; }
else dist = x; }
else dist = x; }
else
if (x < dist) dist = x; }
k = 0;
if ((ent->client->pers.routeindex-1) >= 0 &&
(Route[ent->client->pers.routeindex].state == 4
|| Route[ent->client->pers.routeindex].state == 5)) {
Get_RouteOrigin(ent->client->pers.routeindex-1,v);
if (fabs(v[2]-ent->s.origin[2]) <= JumpMax) {
if (ent->client->waterstate < 2 && Route[ent->client->pers.routeindex].ent->nextthink > level.time)
k = 1; } }
if (k && !(ent->client->movestate & (0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800))) {
if ((ent->client->pers.routeindex+1) < TotalRouteNodes) {
Get_RouteOrigin(ent->client->pers.routeindex+1,v);
if ((v[2]-ent->s.origin[2]) > JumpMax) {
if ((Route[ent->client->pers.routeindex].ent->union_ent->s.origin[2] - ent->s.origin[2]) > JumpMax) {
ent->client->waiting_obj = Route[ent->client->pers.routeindex].ent;
ent->client->movestate |= 0x00000400;
k = 0;
for (i = 1;i <=3;i++) {
if (ent->client->pers.routeindex-i >= 0) {
Get_RouteOrigin(ent->client->pers.routeindex-i,v);
if (ent->client->waiting_obj->absmax[0] < (v[0]+ent->mins[0])) k = 1;
else if (ent->client->waiting_obj->absmax[1] < (v[1]+ent->mins[1])) k = 1;
else if (ent->client->waiting_obj->absmin[0] > (v[0]+ent->maxs[0])) k = 1;
else if (ent->client->waiting_obj->absmin[1] > (v[1]+ent->maxs[1])) k = 1;
if (k) break; } }
if (k)
VectorCopy(v,ent->client->movtarget_pt);
else
Get_RouteOrigin(ent->client->pers.routeindex-1,ent->client->movtarget_pt);
goto VCHCANSEL; } } } } } } }
else
if (ent->client->pers.routeindex >= TotalRouteNodes) {
ent->client->pers.routeindex = 0;
ent->client->routetrace = false; } }
else {
ent->client->pers.routeindex = 0;
ent->client->routetrace = false; } }
if (!(ent->client->movestate & 0x00000020) && (!ent->groundentity || ent->groundentity != ent->client->waiting_obj))
if (!(ent->client->waiting_obj && ent->client->waiting_obj->use == door_use)) {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; }
if (ent->groundentity && !(ent->client->movestate & (0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800))) {
it_ent = ent->groundentity;
if (it_ent->classname[0] == 'f') {
if (it_ent->use == Use_Plat) {
if (it_ent->pos1[2] > it_ent->pos2[2]
&& ((it_ent->moveinfo.state == 2 && it_ent->velocity[2] > 0) || it_ent->moveinfo.state == 1)) {
ent->client->waiting_obj = it_ent;
ent->client->movestate |= 0x00000040;
if (ent->client->routetrace) {
if (Route[ent->client->pers.routeindex].ent == ent->client->waiting_obj)
if (Route[ent->client->pers.routeindex].state == 4) {
if (ent->client->waiting_obj->union_ent->s.origin[2] >(ent->s.origin[2]+32)) {
ent->client->movestate &= ~0x00000040;
ent->client->movestate |= 0x00000400; }
else
ent->client->pers.routeindex++; } } } }
else
if (it_ent->use == train_use
&& it_ent->nextthink >= level.time
&& ((it_ent->s.origin[2]-it_ent->s.old_origin[2]) > 0 || ent->client->routetrace)) {
if (ent->client->routetrace && ent->client->pers.routeindex > 0) {
j = 0;
k = ent->client->pers.routeindex-1;
for (i=0;i<3;i++) {
if ((k+i) < TotalRouteNodes) {
if (Route[k+i].state == 5) {
if (Route[k+i].ent == it_ent)
j = 1;
else
if (it_ent->trainteam) {
e = it_ent->trainteam;
while (1) {
if (e == it_ent) break;
if (e == Route[k+i].ent) {
j = 1;
it_ent = e;
Route[k+i].ent = e;
break; }
e = e->trainteam; } }
else
if (it_ent->target_ent) {
if (VectorCompare(Route[k+i].Tcourner,it_ent->target_ent->s.origin)) {
j = 1;
break; } }
if (j) break; } }
else break; }
if (j) {
ent->client->movestate |= 0x00000200;
ent->client->waiting_obj = it_ent;
ent->client->pers.routeindex = k+i+1; } }
else {
if ((it_ent->s.origin[2]-it_ent->s.old_origin[2]) > 0) {
ent->client->movestate |= 0x00000200;
ent->client->waiting_obj = it_ent; }
else
if ((it_ent->s.origin[2]-it_ent->s.old_origin[2]) > -2
&& trace_priority) {
ent->client->movestate |= 0x00000200;
ent->client->waiting_obj = it_ent; }
else
ent->client->movestate |= 0x00000010; } } } }
if ((ent->client->movestate & 0x00000010) && ent->groundentity) {
if (ent->client->movestate & 0x00000040) {
if (ent->groundentity->use == Use_Plat) {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; } }
else
if (ent->client->movestate & 0x00000200) {
if (ent->groundentity->use == train_use) {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; } }
else
if (ent->client->movestate & (0x00000080|0x00000100)) {
if (ent->groundentity->use == door_use) {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; } }
else {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; } }
else
if ((ent->client->movestate & (0x00000040|0x00000400|0x00000080|0x00000100))
&& !(ent->client->movestate & 0x00000010)) {
k = 0;
if (ent->client->movestate & (0x00000080|0x00000100)) {
if (ent->client->movestate & 0x00000080) {
if (ent->client->waiting_obj->moveinfo.state & 2|1)
k = 1; }
else {
if (ent->client->waiting_obj->moveinfo.state & 0|3)
k = 1; } }
else
if (ent->client->movestate & 0x00000400) {
if (Route[ent->client->pers.routeindex].state == 5) {
if (!TraceX(ent,Route[ent->client->pers.routeindex].ent->union_ent->s.origin))
k = 1;
if ((Route[ent->client->pers.routeindex].ent->union_ent->s.origin[2]+8-ent->s.origin[2]) > JumpMax)
k = 1; }
else {
if ((ent->client->waiting_obj->union_ent->s.origin[2] - ent->s.origin[2]) > JumpMax)
k = 1; }
if (ent->client->pers.routeindex-1 > 0 && ent->client->waterstate < 2) {
Get_RouteOrigin(ent->client->pers.routeindex -1 ,trmin);
if ((trmin[2]-ent->s.origin[2]) > JumpMax && (v[2]-ent->s.origin[2]) > JumpMax)
k = 0; } }
else {
if (ent->client->waiting_obj->moveinfo.state & 2|1)
k = 1;
if (ent->client->waiting_obj->moveinfo.state == 1)
plat_go_up(ent->client->waiting_obj);
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (ent->s.origin[2] > v[2])
k = 2; } }
if (k != 1) {
if (k == 2)
ent->client->movestate |= 0x00000010;
else {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; } }
else {
if (ent->client->movestate & 0x00000400) {
k = 0;
if (ent->client->pers.routeindex-1 > 0) {
VectorCopy(ent->client->movtarget_pt,trmax);
trmax[2] = 0;
k = 1; }
if (!k)
goto VCHCANSEL; }
else {
trmax[0] = (ent->client->waiting_obj->absmin[0]+ent->client->waiting_obj->absmax[0])*0.5;
trmax[1] = (ent->client->waiting_obj->absmin[1]+ent->client->waiting_obj->absmax[1])*0.5;
trmax[2] = 0; }
VectorSubtract(trmax,ent->s.origin,temppos);
yaw = temppos[2];
temppos[2] = 0;
x = VectorLength(temppos);
if (x == 0)
goto VCHCANSEL;
if (x < dist)
dist = x;
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos); } }
else
if (ent->client->movestate & 0x00000200) {
i = 0;
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if ((ent->client->pers.routeindex-1) >= 0) {
if (Route[ent->client->pers.routeindex-1].state != 5)
i = 1; }
else
i = 1;
if (TraceX(ent,v)) {
if ((v[2]-ent->s.origin[2]) <= JumpMax)
i = 1;
else
ent->client->routelocktime = level.time+1.5; }
else
ent->client->routelocktime = level.time+1.5; }
else
if (j ||(ent->client->waiting_obj->s.origin[2]-ent->client->waiting_obj->s.old_origin[2]) <= 0) {
ent->client->movestate |= 0x00000010;
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800); }
else {
k = 0;
if (ent->client->routetrace) {
tr = gi.trace(ent->s.origin,NULL,NULL,v,ent,MASK_BOTSOLIDX);
if (tr.ent == ent->client->waiting_obj) {
tr = gi.trace(v,NULL,NULL,ent->s.origin,ent,MASK_BOTSOLIDX);
if (tr.ent == ent->client->waiting_obj) {
VectorSubtract(v,ent->s.origin,temppos);
k = 1; } } }
if (!k) {
VectorCopy(ent->client->waiting_obj->union_ent->s.origin,trmax);
trmax[2] += 8;
VectorSubtract(trmax,ent->s.origin,temppos);
yaw = temppos[2];
temppos[2] = 0;
x = VectorLength(temppos);
if (x < dist)
dist = x; }
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos); }
goto GOMOVE; }
else
if (ent->client->movestate & 0x00000020) {
if (!trace_priority || ent->client->waiting_obj->moveinfo.state == 0) {
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->waiting_obj = NULL; }
else
if (ent->client->waiting_obj->moveinfo.state & 1|2) {
VectorSubtract(ent->client->movtarget_pt,ent->s.origin,temppos);
temppos[2] = 0;
dist *= 0.25;
if (VectorLength(temppos) < 10 || VectorCompare(ent->s.origin,ent->client->movtarget_pt)) {
if (!ent->client->waiting_obj->union_ent) {
trmin[0] = (ent->client->waiting_obj->absmin[0]+ent->client->waiting_obj->absmax[0])*0.5;
trmin[1] = (ent->client->waiting_obj->absmin[1]+ent->client->waiting_obj->absmax[1])*0.5;
trmin[2] = (ent->client->waiting_obj->absmin[2]+ent->client->waiting_obj->absmax[2])*0.5; }
else
VectorCopy(ent->client->waiting_obj->union_ent->s.origin,trmin);
trmin[2] += 8;
VectorSubtract(trmin,ent->s.origin,temppos);
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos);
if (trace_priority < 2) {
ent->s.angles[YAW] = ent->client->moveyaw;
ent->s.angles[PITCH] = Get_pitch(temppos); }
goto VCHCANSEL; }
else {
if (trace_priority < 3)
ent->client->moveyaw = Get_yaw(temppos);
if (!ent->client->waiting_obj->union_ent) {
trmin[0] = (ent->client->waiting_obj->absmin[0]+ent->client->waiting_obj->absmax[0])*0.5;
trmin[1] = (ent->client->waiting_obj->absmin[1]+ent->client->waiting_obj->absmax[1])*0.5;
trmin[2] = (ent->client->waiting_obj->absmin[2]+ent->client->waiting_obj->absmax[2])*0.5; }
else
VectorCopy(ent->client->waiting_obj->union_ent->s.origin,trmin);
trmin[2] += 8;
VectorSubtract(trmin,ent->s.origin,temppos);
if (trace_priority < 2) {
ent->s.angles[YAW] = Get_yaw(temppos);
ent->s.angles[PITCH] = Get_pitch(temppos); } } } }
GOMOVE:
if (!ent->groundentity && !ent->waterlevel) {
if (ent->velocity[2] > 300 && !(ent->client->movestate & (0x00000008|0x00000002|0x00000004)))
ent->velocity[2] = 300;
k = (ent->client->ps.pmove.pm_flags & PMF_DUCKED)?1:0;
for (x = 0; x < 90; x += 10) {
dist = 30*ent->moveinfo.speed;
yaw = ent->client->moveyaw+x;
if (yaw > 180) yaw -= 360;
if (Bot_TestMove(ent,yaw,temppos,dist,&bottom)) {
if (bottom <= 24 && bottom > 0)
if (ent->velocity[2] <= 10) {
VectorCopy(temppos,ent->s.origin);
break; }
if (!ent->waterlevel && ent->s.origin[2] > ent->s.old_origin[2]
&& ent->client->routetrace
&& !(ent->client->movestate & 0x00000001|(0x00000008|0x00000002|0x00000004))
&& (ent->client->pers.routeindex+1) < TotalRouteNodes
&& ent->velocity[2] >= 100
&& ent->velocity[2] < (100+ent->gravity*sv_gravity->value*0.1)) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
Get_RouteOrigin(ent->client->pers.routeindex+1,vv);
k = 0;
j = Bot_TestMove(ent,yaw,trmin,16,&f1);
VectorSubtract(v,ent->s.origin,trmin);
if ((vv[2]-v[2]) > JumpMax)
k = 1;
else
if ((v[2]-ent->s.origin[2]) > JumpMax)
k = 2;
else
if (!TargetJump_Chk(ent,vv,0) && VectorLength(trmin) < 64) {
if (TargetJump_Chk(ent,vv,ent->velocity[2]))
k = 1; }
if (!j)
k = 0;
else
if (f1 > 10 && f1 < -10)
k = 0;
if (k) {
if (k == 2)
VectorCopy(v,vv);
if (TargetJump(ent,vv)) {
VectorSubtract(vv,ent->s.origin,v);
ent->client->moveyaw = Get_yaw(v);
if (ent->velocity[2] > 300)
ent->client->movestate |= 0x00000008;
if (k == 1)
ent->client->pers.routeindex++;
break; } } }
if (bottom <= 0) {
VectorCopy(temppos,ent->s.origin);
if (i == 2)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
else
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; }
else
ent->moveinfo.speed = 0.3; }
else {
ent->moveinfo.speed = 0.3; }
if (x == 0) continue;
yaw = ent->client->moveyaw-x;
if (yaw < -180) yaw += 360;
if (Bot_TestMove(ent,yaw,temppos,dist,&bottom)) {
if (bottom <= 24 && bottom >0 && ent->velocity[2] <= 10) {
VectorCopy(temppos,ent->s.origin);
break; }
if (!ent->waterlevel && ent->s.origin[2] > ent->s.old_origin[2]
&& ent->client->routetrace
&& !(ent->client->movestate & 0x00000001|(0x00000008|0x00000002|0x00000004))
&& (ent->client->pers.routeindex+1) < TotalRouteNodes
&& ent->velocity[2] >= 100
&& ent->velocity[2] <(100+ent->gravity*sv_gravity->value*0.1)) {
Get_RouteOrigin(ent->client->pers.routeindex ,v);
Get_RouteOrigin(ent->client->pers.routeindex+1,vv);
k = 0;
j = Bot_TestMove(ent,yaw,trmin,16,&f1);
VectorSubtract(v,ent->s.origin,trmin);
if ((vv[2]-v[2]) > JumpMax)
k = 1;
else
if ((v[2]-ent->s.origin[2]) > JumpMax)
k = 2;
else
if (!TargetJump_Chk(ent,vv,0) && VectorLength(trmin) < 64) {
if (TargetJump_Chk(ent,vv,ent->velocity[2]))
k = 1; }
if (!j)
k = 0;
else
if (f1 > 10 && f1 < -10)
k = 0;
if (k) {
if (k == 2)
VectorCopy(v,vv);
if (TargetJump(ent,vv)) {
VectorSubtract(vv,ent->s.origin,v);
ent->client->moveyaw = Get_yaw(v);
if (ent->velocity[2] > 300)
ent->client->movestate |= 0x00000008;
if (k == 1)
ent->client->pers.routeindex++;
break; } } }
if (bottom <= 0) {
VectorCopy(temppos,ent->s.origin);
if (i == 2)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
else
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; }
else
ent->moveinfo.speed = 0.3; }
else
ent->moveinfo.speed = 0.3; }
if (x >= 90) {
if (trace_priority < 2)
ent->s.angles[YAW] += ((random()-0.5)*360);
if (ent->s.angles[YAW]>180)
ent->s.angles[YAW] -= 360;
else
if (ent->s.angles[YAW]< -180)
ent->s.angles[YAW] += 360; }
goto VCHCANSEL; }
waterjumped = false;
if (ent->groundentity || ent->waterlevel) {
if (ent->groundentity && ent->waterlevel <= 0)
k = 1;
else
if (ent->waterlevel) {
k = 2;
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
VectorSubtract(v,ent->s.origin,vv);
vv[2] = 0;
if (v[2] < ent->s.origin[2] && VectorLength(vv) < 24)
k = 0; }
if (ent->waterlevel == 3)
k = 0; }
else
if (ent->waterlevel)
k = 0;
else
k = 1;
if (k)
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
k = 0;
f1 = (ent->client->waterstate)?-8192:-JumpMax;
if (ent->client->nextcheck < (level.time+1.0)) {
VectorSubtract(ent->client->my_old_origin,ent->s.origin,temppos);
if (VectorLength(temppos) < 64) {
if (ent->client->routetrace) {
ent->client->routetrace = false;
ent->client->pers.routeindex++; }
else
f1 = -300; }
if (ent->client->nextcheck < level.time) {
VectorCopy(ent->s.origin,ent->client->my_old_origin);
ent->client->nextcheck = level.time+4.0; } }
f3 = 20;
if (ent->client->routetrace)
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (ent->waterlevel && ent->client->routetrace) {
if (v[2]+20 <= ent->s.origin[2]) {
f2 = 20; f3 = 0; }
else
f2 = JumpMax; }
else
f2 = JumpMax;
ladderdrop = true;
for (x = 0; x <= 180 && dist != 0; x += 10) {
yaw = ent->client->moveyaw+x;
if (yaw > 180) yaw -= 360;
if (j = Bot_TestMove(ent,yaw,temppos,dist,&bottom)) {
if (x == 0 && !ent->waterlevel && !(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) {
if (ent->client->routetrace) {
if ((v[2] -(ent->s.origin[2]+bottom)) > f2 || (bottom > 20 && v[2] > ent->s.origin[2])) {
ladderdrop = false;
if (Bot_Fall(ent,temppos,dist) && !ent->client->waterstate) {
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; }
if ((v[2]-ent->s.origin[2]) <= JumpMax) {
if (Route[ent->client->pers.routeindex].state == 5 && ent->client->waterstate < 2) break;
if (ent->client->pers.routeindex > 0)
if (Route[ent->client->pers.routeindex-1].state == 5
&& Route[ent->client->pers.routeindex-1].ent == ent->groundentity) break; } }
else
if (ent->groundentity) {
if (ent->groundentity->use == rotating_use) {
if (Bot_Fall(ent,temppos,dist)) {
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; } }
else
if (Route[ent->client->pers.routeindex].state == 1) {
if (!TraceX(ent,v)) break;
if (!HazardCheck(ent,v)) break;
if (!BankCheck(ent,v)) break;
if (Bot_Fall(ent,temppos,dist)) {
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; } } } } }
if (bottom > 20 && bottom <= f2 && j == 1 && k && !(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) {
ent->moveinfo.speed = 0.15;
if (k == 1)
ent->velocity[2] = 300;
else {
ent->moveinfo.speed = 0.1;
if (ent->velocity[2] < 300 || VectorCompare(ent->s.origin,ent->s.old_origin)) {
ent->velocity[2] = 300;
ent->client->movestate |= 0x00000004; }
goto VCHCANSEL; }
SetBotAnim(ent);
ent->client->moveyaw = yaw;
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; }
else
if (bottom <= f3 && (bottom >= f1 || ent->waterlevel)) {
if (bottom < 0 && !ent->client->waterstate) {
f2 = 0.1*(ent->velocity[2]-ent->gravity*sv_gravity->value*0.1);
if (bottom >= f2 && ent->velocity[2] < 0)
temppos[2] += bottom;
else
temppos[2] += f2; }
VectorCopy(temppos,ent->s.origin);
if (f1 > -52)
ent->moveinfo.speed = 0.25;
if (j != 1)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
else
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
if (x > 30 || !ent->client->routetrace) {
f2 = ent->client->moveyaw;
ent->client->moveyaw = yaw;
if (f2 == ent->s.angles[YAW] && trace_priority < 2)
ent->s.angles[YAW] = yaw; }
break; }
else
if (bottom < f1 && !ent->client->waterstate && x <= 30) {
if (ladderdrop && bottom != -9999)
if (ent->client->ground_contents & CONTENTS_LADDER) {
VectorCopy(temppos,ent->s.origin);
ent->client->moveyaw = yaw;
ent->moveinfo.speed = 0.2;
goto VCHCANSEL; }
if (ladderdrop && bottom < 0)
if (!ent->client->waterstate) {
if (Bot_moveW(ent,yaw,temppos,dist,&bottom)) {
iyaw = -41;
if (bottom > -20 && iyaw < -40) {
VectorCopy(temppos,ent->s.origin);
break; } } }
if (Bot_Fall(ent,temppos,dist))
break; } }
if (x == 0 && (ent->client->battlemode & (0x00020000|0x00040000)))
ent->client->battlemode &= ~(0x00020000|0x00040000);
if (x == 0 || x == 180) continue;
yaw = ent->client->moveyaw-x;
if (yaw < -180) yaw += 360;
if (j = Bot_TestMove(ent,yaw,temppos,dist,&bottom)) {
f2 = (ent->client->waterstate == 1)?100:JumpMax;
if (bottom > 20 && bottom <= f2 && j == 1 && k && !(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) {
ent->moveinfo.speed = 0.15;
if (k == 1)
ent->velocity[2] = 300;
else {
ent->moveinfo.speed = 0.1;
if (ent->velocity[2] < 300 || VectorCompare(ent->s.origin,ent->s.old_origin)) {
ent->velocity[2] = 300;
ent->client->movestate |= 0x00000004; }
goto VCHCANSEL; }
SetBotAnim(ent);
ent->client->moveyaw = yaw;
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
break; }
else
if (bottom <= f3 && (bottom >= f1 || ent->waterlevel)) {
if (bottom < 0 && !ent->client->waterstate) {
f2 = 0.1*(ent->velocity[2]-ent->gravity*sv_gravity->value*0.1);
if (bottom >= f2 && ent->velocity[2] < 0)
temppos[2] += bottom;
else
temppos[2] += f2; }
VectorCopy(temppos,ent->s.origin);
if (f1 > -52)
ent->moveinfo.speed = 0.25;
if (j != 1)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
else
ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED;
if (x > 30 || !ent->client->routetrace) {
f2 = ent->client->moveyaw;
ent->client->moveyaw = yaw;
if (f2 == ent->s.angles[YAW] && trace_priority < 2)
ent->s.angles[YAW] = yaw; }
break; }
else
if (bottom < f1 && !ent->client->waterstate && x <= 30) {
if (ladderdrop && ent->client->ground_contents & CONTENTS_LADDER && bottom != -9999) {
VectorCopy(temppos,ent->s.origin);
ent->client->moveyaw = yaw;
ent->moveinfo.speed = 0.2;
goto VCHCANSEL; }
if (ladderdrop && bottom < 0 && !ent->client->waterstate) {
if (Bot_moveW(ent,yaw,temppos,dist,&bottom)) {
iyaw = -41;
if (bottom > -54 && iyaw < -40) {
VectorCopy(temppos,ent->s.origin);
break; } } }
if (Bot_Fall(ent,temppos,dist))
break; } } }
if (!ent->client->routetrace && !ent->client->current_enemy)
if (trace_priority < 2)
ent->s.angles[YAW] = yaw;
if (ent->waterlevel && !waterjumped) {
k = 0;
VectorCopy(ent->s.origin,temppos);
if (ent->client->routetrace) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
k = 2;
x = v[2]-ent->s.origin[2];
if (x > 13) x = 13;
else if (x < -13) x = -13;
if (x < 0) {
if (Bot_Watermove(ent,temppos,dist,x)) {
VectorCopy(temppos,ent->s.origin);
k = 1; } }
else
if (x > 0 && ent->client->waterstate == 2)
if (!(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) {
if (ent->velocity[2] < -10)
ent->velocity[2] = 0;
if (Bot_Watermove(ent,temppos,dist,x)) {
VectorCopy(temppos,ent->s.origin);
k = 1; } } }
else
if (ent->air_finished-2.0 < level.time)
if (ent->client->waterstate == 2) {
if (Bot_Watermove(ent,temppos,dist,13)) {
VectorCopy(temppos,ent->s.origin);
k = 1; }
else
k = 2; }
if (k == 1)
Get_WaterState(ent);
if (ent->client->routetrace)
if (v[2] == ent->s.origin[2])
k = 3;
if ((!ent->groundentity && !ent->client->waterstate && k && ent->velocity[2] < 1)
||(ent->client->waterstate == 2 && (ent->client->ps.pmove.pm_flags & PMF_DUCKED))) {
if (Bot_Watermove(ent,temppos,dist,-7) && k != 3)
VectorCopy(temppos,ent->s.origin); }
if (ent->client->waterstate == 2)
ent->moveinfo.decel = level.time;
else
if (!k) {
if ((level.time-ent->moveinfo.decel) > 4.0)
if (!ent->client->routetrace) {
ent->velocity[2] = -200;
ent->moveinfo.decel = level.time; } }
if (ent->groundentity && ent->waterlevel == 1) {
VectorSubtract(ent->s.origin,ent->s.old_origin,temppos);
if (!temppos[0] && !temppos[1] && !temppos[2])
ent->velocity[2] += 80; } }
else
if (ent->client->routetrace && !dist) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
if (v[2] <(ent->s.origin[2]-20))
if (Bot_Watermove(ent,temppos,dist,-20))
VectorCopy(temppos,ent->s.origin); } }
if (!ent->client->routetrace && trace_priority && random() < 0.2) {
VectorCopy(ent->s.origin,v);
VectorCopy(ent->mins,touchmin);
touchmin[2] += 16;
VectorCopy(ent->maxs,touchmax);
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
touchmax[2] = 0;
else
v[2] += 20;
if (random() < 0.5) {
f1 = ent->client->moveyaw+90;
if (f1 > 180) iyaw -= 360;
f2 = ent->client->moveyaw+135;
if (f2 > 180) iyaw -= 360; }
else {
f1 = ent->client->moveyaw-90;
if (f1 < 180) iyaw += 360;
f2 = ent->client->moveyaw-135;
if (f2 < 180) iyaw += 360; }
yaw = DEG2RAD(f1);
trmin[0] = cos(yaw)*128;
trmin[1] = sin(yaw)*128;
trmin[2] = 0;
VectorAdd(v,trmin,trmax);
tr = gi.trace(v, NULL, NULL, trmax, ent, MASK_BOTSOLIDX);
x = tr.fraction;
yaw = DEG2RAD(f2);
trmin[0] = cos(yaw)*128;
trmin[1] = sin(yaw)*128;
trmin[2] = 0;
VectorAdd(v,trmin,trmax);
tr = gi.trace(v, NULL, NULL, trmax, ent, MASK_BOTSOLIDX);
if (x > tr.fraction && x > 0.5)
ent->client->moveyaw = f1; }
it_ent = NULL;
k = 0;
VectorCopy(ent->absmin, touchmin);
VectorCopy(ent->absmax, touchmax);
touchmin[0] -= 48;
touchmin[1] -= 48;
touchmin[2] -= 5;
touchmax[0] += 48;
touchmax[1] += 48;
if (i=gi.BoxEdicts(touchmin ,touchmax,touch,1024,1)) {
for (j=i-1;j>=0;j--) {
trent = touch[j];
if (trent->classname) {
if (trent->use == button_use) {
k = 1;
it_ent = trent;
break; }
else
if (trent->use == door_use || trent->use == rotating_use) {
if (!trent->targetname && !trent->takedamage)
if (ent->groundentity != trent) {
k = 2;
it_ent = trent;
break; } } } } }
if (it_ent && k == 1) {
if (it_ent->use && it_ent->moveinfo.state == 1 && !it_ent->health) {
k = 0;
if (ent->client->routetrace && ent->client->pers.routeindex-1 > 0) {
k = 1;
i = ent->client->pers.routeindex;
if (Route[i].state == 7)
k = 0;
else
if (Route[--i].state == 7)
k = 0;
if (!k && Route[i].ent == it_ent)
ent->client->pers.routeindex = i+1;
else
k = 1; }
if (!k && it_ent->target) {
str = it_ent->target;
e = &g_edicts[(int)maxclients->value+1];
for (i=maxclients->value+1; i<globals.num_edicts; i++, e++) {
if (!e->inuse || !e->targetname) continue;
if (!stricmp(str, e->targetname)) {
if (e->classname[0] == 't') {
if (e->use == trigger_relay_use) {
if (e->target) {
str = e->target;
e = &g_edicts[(int)maxclients->value];
i=maxclients->value;
continue; } } }
else
if (e->classname[0] == 'f') {
it_ent->use(it_ent,ent,it_ent);
if (e->use == door_use || e->use == rotating_use) {
k = 0;
if (!ent->client->routetrace) {
v[0] = (it_ent->absmin[0]+it_ent->absmax[0])*0.5;
v[1] = (it_ent->absmin[1]+it_ent->absmax[1])*0.5;
v[2] = (it_ent->absmin[2]+it_ent->absmax[2])*0.5;
VectorSubtract(it_ent->union_ent->s.origin,v,temppos);
VectorScale(temppos, 3, v);
VectorAdd(ent->s.origin,v,ent->client->movtarget_pt); }
else
VectorCopy(ent->s.origin,ent->client->movtarget_pt);
if (fabs(e->moveinfo.start_origin[2]-e->moveinfo.end_origin[2]) > JumpMax) {
if (!e->union_ent) {
it = item_navi3;
trent = G_Spawn();
trent->classname = it->classname;
trent->s.origin[0] = (e->absmin[0]+e->absmax[0])*0.5;
trent->s.origin[1] = (e->absmin[1]+e->absmax[1])*0.5;
trent->s.origin[2] = e->absmax[2]+16;
trent->union_ent = e;
e->union_ent = trent;
SpawnItem3(trent, it); }
else {
trent = e->union_ent;
trent->solid = SOLID_TRIGGER;
trent->svflags &= ~SVF_NOCLIENT; }
trent->target_ent = ent;
if (e->spawnflags & 32) {
f1 = e->moveinfo.start_origin[2]-e->moveinfo.end_origin[2];
k = 1; }
else {
f1 = e->moveinfo.start_origin[2]-e->moveinfo.end_origin[2];
if (f1 > 0) {
if (e->moveinfo.state & 1|2) {
if (fabs(trent->s.origin[2]-ent->s.origin[2]) < JumpMax)
k = 1; } }
else {
if (e->moveinfo.state & 1|2) {
if (fabs(trent->s.origin[2]-ent->s.origin[2]) < JumpMax)
k = 1; } } } }
if (!k) {
ent->client->waiting_obj = e;
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->movestate |= 0x00000020; }
else {
if ((e->union_ent->s.origin[2]+8-ent->s.origin[2]) > JumpMax) {
ent->client->routetrace = false;
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800); } }
break; } } } } }
else
if (!k)
it_ent->use(it_ent,ent,it_ent); } }
else
if (it_ent && k == 2) {
if (it_ent->moveinfo.state == 1) {
if (it_ent->flags & FL_TEAMSLAVE)
it_ent->teammaster->use(it_ent->teammaster,ent,it_ent->teammaster);
else
it_ent->use(it_ent,ent,it_ent); }
if (it_ent->moveinfo.state == 1) {
VectorCopy(ent->s.origin,ent->client->movtarget_pt);
ent->client->waiting_obj = it_ent;
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->movestate |= 0x00000020;
if (it_ent->flags & FL_TEAMSLAVE) {
trmin[0] = (it_ent->teammaster->absmin[0]+it_ent->teammaster->absmax[0])*0.5;
trmin[1] = (it_ent->teammaster->absmin[1]+it_ent->teammaster->absmax[1])*0.5;
trmax[0] = (it_ent->absmin[0]+it_ent->absmax[0])*0.5;
trmax[1] = (it_ent->absmin[1]+it_ent->absmax[1])*0.5;
temppos[0] = (trmin[0]+trmax[0])*0.5;
temppos[1] = (trmin[1]+trmax[1])*0.5;
if (trace_priority < 2)
ent->s.angles[YAW] = Get_yaw(temppos); }
else {
trmax[0] = (it_ent->absmin[0]+it_ent->absmax[0])*0.5;
trmax[1] = (it_ent->absmin[1]+it_ent->absmax[1])*0.5;
VectorSubtract(trmax,ent->s.origin,temppos);
if (trace_priority < 2)
ent->s.angles[YAW] = Get_yaw(temppos); } }
else
if (it_ent->moveinfo.state == 2) {
VectorCopy(ent->s.origin,ent->client->movtarget_pt);
ent->client->waiting_obj = it_ent;
ent->client->movestate &= ~(0x00000010|0x00000020|0x00000400|0x00000040|0x00000080|0x00000100|0x00000200|0x00000800);
ent->client->movestate |= 0x00000020; } }
VCHCANSEL:
front = left = right = NULL;
k = 0;
if (ent->client->routetrace)
if (ent->client->pers.routeindex+1 < TotalRouteNodes) {
Get_RouteOrigin(ent->client->pers.routeindex+1,v);
if (v[2]-ent->s.origin[2] >= 32)
k = 1; }
if (k && trace_priority && !(ent->client->ps.pmove.pm_flags & PMF_DUCKED)) {
tempflag = 0;
VectorCopy(ent->mins,trmin);
VectorCopy(ent->maxs,trmax);
trmin[2] += 20;
iyaw = ent->client->moveyaw;
yaw = DEG2RAD(iyaw);
touchmin[0] = cos(yaw)*32;
touchmin[1] = sin(yaw)*32;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,touchmax);
tr = gi.trace(ent->s.origin, trmin,ent->maxs, touchmax,ent, MASK_BOTSOLID);
front = tr.ent;
if (tr.contents & CONTENTS_LADDER)
tempflag = 1;
if (!tempflag && !ent->client->waterstate) {
trmax[2] += 32;
tr = gi.trace(ent->s.origin, trmin,trmax, touchmax,ent, MASK_BOTSOLID);
if (tr.contents & CONTENTS_LADDER)
tempflag = 2; }
if (!tempflag && ent->groundentity) {
Get_RouteOrigin(ent->client->pers.routeindex,v);
v[2] = ent->s.origin[2];
tr = gi.trace(ent->s.origin, trmin,ent->maxs, v,ent, MASK_BOTSOLID);
if (tr.contents & CONTENTS_LADDER)
tempflag = 3; }
if (tempflag==0) {
iyaw = ent->client->moveyaw+90;
if (iyaw > 180) iyaw -= 360;
yaw = DEG2RAD(iyaw);
touchmin[0] = cos(yaw)*32;
touchmin[1] = sin(yaw)*32;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,touchmax);
tr = gi.trace(ent->s.origin, trmin,ent->maxs, touchmax,ent, MASK_BOTSOLID);
right = tr.ent;
if (tr.contents & CONTENTS_LADDER)
tempflag = 1; }
if (tempflag==0) {
iyaw = ent->client->moveyaw-90;
if (iyaw < -180) iyaw += 360;
yaw = DEG2RAD(iyaw);
touchmin[0] = cos(yaw)*32;
touchmin[1] = sin(yaw)*32;
touchmin[2] = 0;
VectorAdd(ent->s.origin,touchmin,touchmax);
tr = gi.trace(ent->s.origin, trmin,ent->maxs, touchmax,ent, MASK_BOTSOLID);
left = tr.ent;
if (tr.contents & CONTENTS_LADDER)
tempflag = 1; }
if (tempflag) {
VectorCopy(tr.endpos,trmax);
VectorCopy(trmax,touchmax);
touchmax[2] += 8192;
tr = gi.trace(trmax, trmin,ent->maxs, touchmax,ent, MASK_SOLID);
e = tr.ent;
k = 0;
VectorCopy(tr.endpos,temppos);
VectorAdd(tr.endpos,touchmin,touchmax);
tr = gi.trace(temppos, trmin,ent->maxs, touchmax,ent, MASK_BOTSOLID);
if (e && e->use == door_use) k = 1;
if ((!(tr.contents & CONTENTS_LADDER) || k)) {
ent->velocity[0] = 0;
ent->velocity[1] = 0;
if (ent->client->moveyaw == iyaw || ent->client->routetrace) {
if (ent->client->moveyaw != iyaw)
ent->client->moveyaw = iyaw;
ent->s.angles[YAW] = ent->client->moveyaw;
if (tempflag != 3)
VectorCopy(trmax,ent->s.origin);
ent->client->movestate |= 0x00000001;
ent->s.angles[YAW] = ent->client->moveyaw;
ent->s.angles[PITCH] = -29;
if (tempflag == 2) {
ent->velocity[2] = 300;
SetBotAnim(ent);
ent->client->movestate |= (0x00000008|0x00000002|0x00000004);
ent->moveinfo.speed = 0; }
else
if (tempflag == 3) {
ent->velocity[2] = 300;
SetBotAnim(ent);
ent->client->movestate |= (0x00000008|0x00000002|0x00000004);
ent->moveinfo.speed = 30; }
ent->velocity[2] = 200; }
else {
ent->client->moveyaw = iyaw;
ent->s.angles[YAW] = ent->client->moveyaw; } } } }
VCHCANSEL_L:
if (ent->client->battleduckcnt > 0)
if (ent->groundentity)
if (ent->velocity[2] < 10) {
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
ent->client->battleduckcnt--; }
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) {
ent->client->duckedtime = 0;
ent->maxs[2] = 4;
ent->viewheight = -2; }
else {
if (ent->client->duckedtime < 1)
ent->client->duckedtime += 0.1;
ent->maxs[2] = 32;
ent->viewheight = 22; }
VectorCopy(ent->s.angles,ent->client->v_angle);
if (ent->s.angles[PITCH] < -29)
ent->s.angles[PITCH] = -29;
else
if (ent->s.angles[PITCH] > 29)
ent->s.angles[PITCH] = 29;
gi.linkentity(ent);
G_TouchTriggers(ent);
RandomChat(ent);
}
//==============================================
void BotThink(edict_t *ent) {
// Reset bot's enemy info after enemy died
if (ent->client->current_enemy)
if (!G_ClientInGame(ent->client->current_enemy)) {
ent->client->battleduckcnt = 0;
ent->client->current_enemy = NULL;
ent->client->combatstate &= ~0x00000001;
ent->client->battlemode = 0x00000000; }
// bot dead? put'em back into game
if (!G_ClientNotDead(ent)) {
ent->s.modelindex2 = 0;
ent->client->routetrace = false;
if (ent->client->respawn_time <= level.time) {
ent->client->respawn_time = level.time;
PutClientInServer(ent); }
ent->nextthink = level.time+FRAMETIME;
return; }
BotAI(ent);
// Quit camping if health below 10%
if (ent->health < 10)
ent->client->camptime = level.time;
// Bots will tend to camp at Railgun
if (ent->client->camptime > level.time) {
VectorCopy(ent->client->lastorigin,ent->s.origin);
if (ent->client->campitem == item_railgun) {
// Force railgun use if camping at rail else stop camping here
if (ent->client->pers.weapon != item_railgun && HasAmmoForWeapon(ent,item_railgun)) {
ent->client->newweapon = item_railgun;
ChangeWeapon(ent); }
else
ent->client->camptime = level.time; }
else
// Bots will tend to camp at MegaHealth
if (ent->client->campitem == item_health_mega) {
if (random() < 0.85)
ent->client->ps.pmove.pm_flags |= PMF_DUCKED;
// Prefer using grenadelauncher if camping at megahealth
if (ent->client->pers.weapon != item_grenadelauncher && HasAmmoForWeapon(ent,item_grenadelauncher)) {
ent->client->newweapon = item_grenadelauncher;
ChangeWeapon(ent); } } }
else
if (ent->client->quad_framenum > level.framenum)
// Force SuperShotgun use if quad enabled!
if (ent->client->pers.weapon != item_supershotgun)
if (HasAmmoForWeapon(ent,item_supershotgun)) {
ent->client->newweapon = item_supershotgun;
ChangeWeapon(ent); }
ent->nextthink = level.time+FRAMETIME;
}
//---------------------------------------------------
</C&NBSP;CODE>
Save this new file!
=====================================================
=====================================================
Okay, you MADE IT!! Now
add your new bot.c file to your
project and sit back, smile and compile!
Now, spawn a bunch of
bots into the game and get out
there and KILL THE BASTARDS!!
Have Fun!!
Maj.Bitch
|