3705 lines
108 KiB
C
3705 lines
108 KiB
C
/*****************************************************************
|
|
|
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
|
|
|
..............................................................
|
|
|
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
|
|
|
..............................................................
|
|
|
|
All other files are Copyright(c) Id Software, Inc.
|
|
|
|
Please see liscense.txt in the source directory for the copyright
|
|
information regarding those files belonging to Id Software, Inc.
|
|
|
|
..............................................................
|
|
|
|
Should you decide to release a modified version of Eraser, you MUST
|
|
include the following text (minus the BEGIN and END lines) in the
|
|
documentation for your modification.
|
|
|
|
--- BEGIN ---
|
|
|
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
|
the Eraser Bot homepage, at http://impact.frag.com.
|
|
|
|
This program is a modification of the Eraser Bot, and is therefore
|
|
in NO WAY supported by Ryan Feltrin.
|
|
|
|
This program MUST NOT be sold in ANY form. If you have paid for
|
|
this product, you should contact Ryan Feltrin immediately, via
|
|
the Eraser Bot homepage.
|
|
|
|
--- END ---
|
|
|
|
..............................................................
|
|
|
|
You will find p_trail.c has not been included with the Eraser
|
|
source code release. This is NOT an error. I am unable to
|
|
distribute this file because it contains code that is bound by
|
|
legal documents, and signed by myself, never to be released
|
|
to the public. Sorry guys, but law is law.
|
|
|
|
I have therefore include the compiled version of these files
|
|
in .obj form in the src\Release and src\Debug directories.
|
|
So while you cannot edit and debug code within these files,
|
|
you can still compile this source as-is. Although these will only
|
|
work in MSVC v5.0, linux versions can be made available upon
|
|
request.
|
|
|
|
NOTE: When compiling this source, you will get a warning
|
|
message from the compiler, regarding the missing p_trail.c
|
|
file. Just ignore it, it will still compile fine.
|
|
|
|
..............................................................
|
|
|
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
|
use of this source code. I also am NOT willing to provide any form
|
|
of help or support for this source code. It is provided as-is,
|
|
as a service by me, with no documentation, other then the comments
|
|
contained within the code. If you have any queries, I suggest you
|
|
visit the "official" Eraser source web-board, at
|
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
|
time to time, to answer questions and help with any problems that
|
|
may arise.
|
|
|
|
Otherwise, have fun, and I look forward to seeing what can be done
|
|
with this.
|
|
|
|
-Ryan Feltrin
|
|
-Acrid-
|
|
|
|
*****************************************************************/
|
|
#include "g_local.h"
|
|
#include "bot_procs.h"
|
|
#include "m_player.h"
|
|
#include "g_items.h"
|
|
|
|
#include <sys/timeb.h>
|
|
|
|
int aborted_fire;
|
|
void fire_stab (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int mod);
|
|
void fire_stinger (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage);
|
|
void fire_pellet_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage);
|
|
void fire_napalmrocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage);
|
|
void fire_clusterrocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage);
|
|
void fire_pulse (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod);
|
|
void fire_needle(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread);
|
|
void fire_flamethrower(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius);
|
|
void fire_telsa(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread);
|
|
void fire_armordart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
|
|
void fire_poisondart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
|
|
void fire_infecteddart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
|
|
void fire_lightning (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int kick);
|
|
void fire_shc (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread);
|
|
//int is_quad;
|
|
|
|
void bot_FireWeapon(edict_t *self)
|
|
{
|
|
if (!self->enemy)
|
|
return;
|
|
|
|
if (!self->bot_client) //Gregg added
|
|
return;
|
|
|
|
|
|
//no firing while botfrozen 3/99
|
|
if (level.time < self->frozentime)
|
|
return;
|
|
|
|
//ACRID ADDED
|
|
if (K2_IsProtected(self))
|
|
self->client->protecttime = 0;
|
|
//END
|
|
// set Quad flag
|
|
is_quad = (self->client->quad_framenum > level.framenum);
|
|
|
|
aborted_fire = false;
|
|
self->bot_fire(self);
|
|
|
|
if (!aborted_fire)
|
|
{
|
|
if (!CTFApplyStrengthSound(self))
|
|
if (is_quad)
|
|
gi.sound(self, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
|
|
|
|
self->last_fire = level.time;
|
|
|
|
if (self->maxs[2] == 4)
|
|
self->s.frame = FRAME_crattak1;
|
|
else
|
|
self->s.frame = FRAME_attack1;
|
|
|
|
if (self->bot_fire != botBlaster)
|
|
self->last_reached_trail = level.time;
|
|
}
|
|
// check for no ammo ON CURRENT WEAPON//WORKS
|
|
if ((self->client->pers.weapon != item_blaster) && (!self->client->pers.inventory[self->client->ammo_index]))
|
|
{
|
|
botPickBestWeapon(self);
|
|
botDebugPrint("BOT No Ammo (ACRID)\n");
|
|
}
|
|
|
|
// check for using knife while carring flag
|
|
if ((self->client->pers.weapon == item_knife) &&
|
|
(CarryingFlag(self)))
|
|
{
|
|
botPickBestWeapon(self);
|
|
botDebugPrint("BOT change knife for other (ACRID)\n");
|
|
}
|
|
|
|
/*ACRID CHANGE WEAPONS IF IN ITEMS LIST AND HAVE AMMO AND FIRING BLASTER.
|
|
*/
|
|
// if ( client->pers.inventory[ITEM_INDEX(item_bullets)]
|
|
// && client->pers.inventory[ITEM_INDEX(item_needler)] &&
|
|
// (self->client->pers.inventory[self->client->ammo_index] <=50)
|
|
if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_shells)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_supershotgun)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_shotgun)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_shc)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_poisondart)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_knife)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_infecteddart)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (SHELLS ACRID)\n");
|
|
}
|
|
}
|
|
else if ((self->bot_fire == botNeedler) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_bullets)] >= 50))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new needler switch (NEEDLER ACRID)\n");
|
|
}
|
|
|
|
else if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_slugs)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_sniperrifle)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_railgun)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_lightninggun)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (SLUGS ACRID)\n");
|
|
}
|
|
}
|
|
else if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_bullets)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_machinegun)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_pulsecannon)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_needler)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_ak47)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_pistol)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_chaingun)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (BULLETS ACRID)\n");
|
|
}
|
|
}
|
|
else if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_cells)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_hyperblaster)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_telsacoil)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_flamethrower)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_bfg10k)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (CELLS ACRID)\n");
|
|
}
|
|
}
|
|
else if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_rockets)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_rocketlauncher)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_rocketnapalmlauncher)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_pelletrocketlauncher)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_stingerrocketlauncher)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_rocketclusterlauncher)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (ROCKETS ACRID)\n");
|
|
}
|
|
}
|
|
else if ((self->bot_fire == botBlaster) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_grenades)] != 0))
|
|
{
|
|
if ((self->client->pers.inventory[ITEM_INDEX(item_grenadelauncher)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_handgrenades)] != 0) ||
|
|
(self->client->pers.inventory[ITEM_INDEX(item_grenadelauncher)] != 0))
|
|
{
|
|
botPickBestWeapon(self);
|
|
// botDebugPrint("BOT new fire (GRENADES ACRID)\n");
|
|
}
|
|
}
|
|
//ACRID END FIX CHANGE WEAPON ON AMMO PICK UP
|
|
if ((self->bot_fire == botBlaster) && (self->enemy) && !CarryingFlag(self->enemy))
|
|
{ // abort attacking enemy?
|
|
gclient_t *client;
|
|
|
|
client = self->enemy->client;
|
|
|
|
if (( (self->health < 20) &&
|
|
(self->enemy->health > 15) &&
|
|
((self->enemy->health > self->health) || client->pers.weapon != item_blaster /*ClientHasAnyWeapon(client)*/)))
|
|
{ // abort the attack
|
|
self->enemy = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void bot_Attack(edict_t *self)
|
|
{
|
|
float strafe_speed=BOT_STRAFE_SPEED;
|
|
|
|
if (!self->enemy || (self->health <= 0))// || (self->enemy->health <= 0))
|
|
return;//sentry co fixme
|
|
|
|
if (!self->bot_client) //Gregg added
|
|
return;
|
|
|
|
// see if the enemy is visible
|
|
if ( (self->last_enemy_sight > (level.time - 0.2))
|
|
|| ( (visible(self, self->enemy) && gi.inPVS(self->s.origin, self->enemy->s.origin))
|
|
&& (self->last_enemy_sight = level.time)))
|
|
{
|
|
trace_t trace;
|
|
|
|
// make sure we don't hit a friend
|
|
if (!((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (ctf->value || self->client->team))
|
|
{
|
|
trace = gi.trace (self->s.origin, vec3_origin, vec3_origin, self->enemy->s.origin, self, MASK_PLAYERSOLID);
|
|
|
|
/*if ((self->client->player_class == 2) &&
|
|
trace.ent && trace.ent->client && SameTeam(trace.ent, self) &&
|
|
(trace.ent->disease) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_shells)] != 0))
|
|
{botDebugPrint("trtrace ent im a nurse");}
|
|
|
|
else*/ if (trace.ent && (trace.ent->client) && SameTeam(trace.ent,self))
|
|
//nurse
|
|
{ // we might hit a good guy!
|
|
return;
|
|
}
|
|
}
|
|
|
|
// BFG delayed firing
|
|
if ((self->bot_fire == botBFG) && (self->last_fire > level.time) && (self->last_fire <= (level.time+0.1)))
|
|
{
|
|
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/bfg__f1y.wav"), 1, 2, 0);
|
|
self->last_fire = level.time - FIRE_INTERVAL_BFG + 0.5;
|
|
}
|
|
|
|
if ( ((self->last_fire + self->fire_interval) < level.time)
|
|
&& (self->sight_enemy_time < (level.time - (SIGHT_FIRE_DELAY * ((5 - self->bot_stats->combat*0.5)/5)))))
|
|
{
|
|
bot_FireWeapon(self);
|
|
|
|
if (CTFApplyHaste(self))
|
|
CTFApplyHasteSound(self);
|
|
}
|
|
// can't strafe if bad Combat skills
|
|
if (self->bot_stats->combat == 1)
|
|
strafe_speed = 0;
|
|
|
|
if (self->enemy != self->last_movegoal)
|
|
{ // only strafe slowly, so we don't go too far off course
|
|
strafe_speed = 0;
|
|
}
|
|
else if (self->maxs[2] == 4)
|
|
{
|
|
strafe_speed *= 0.5;
|
|
}
|
|
|
|
// do attack movements, like strafing
|
|
if ((strafe_speed > 0) && self->enemy && self->groundentity && (self->strafe_changedir_time < level.time) && (self->bot_stats->combat > 1))
|
|
{
|
|
self->strafe_dir = !self->strafe_dir;
|
|
self->strafe_changedir_time = level.time + 0.5 + random() * 1.5;
|
|
|
|
// check for ducking or jumping
|
|
if (self->crouch_attack_time < level.time)
|
|
{
|
|
float rnd, dist;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
rnd = random()*4;
|
|
|
|
// if low combat, then skip jumping for a bit
|
|
if (self->bot_stats->combat < rnd)
|
|
{
|
|
self->crouch_attack_time = level.time + 1;
|
|
goto nojump;
|
|
}
|
|
// crouch if far away
|
|
if ((self->maxs[2] > 4) && (dist > 400) && (rnd < 3))
|
|
{
|
|
if (self->bot_stats->combat > 4)
|
|
{
|
|
self->crouch_attack_time = level.time + random()*0.5 + 0.5;
|
|
self->maxs[2] = 4;
|
|
}
|
|
}
|
|
else if ( (dist < 700)
|
|
&& ( (self->last_seek_enemy < level.time)
|
|
|| (entdist(self->last_movegoal, self) > 256))
|
|
&& (CanJump(self))) // jump
|
|
{
|
|
vec3_t right, dest, mins;
|
|
trace_t trace;
|
|
// if combat = 2, jump less frequently
|
|
if ((self->bot_stats->combat >= 3) || (random() < 0.3))
|
|
{
|
|
vec3_t rvec;
|
|
|
|
AngleVectors(self->s.angles, NULL, right, NULL);
|
|
VectorCopy(right, rvec);
|
|
VectorScale(right, ((self->strafe_dir * 2) - 1), right);
|
|
VectorScale(right, BOT_STRAFE_SPEED, right);
|
|
|
|
// check that the jump will be safe
|
|
// VectorAdd(self->mins, tv(0,0,12), mins);
|
|
VectorAdd(self->s.origin, right, dest);
|
|
trace = gi.trace(self->s.origin, mins, self->maxs, dest, self, MASK_SOLID);
|
|
VectorSubtract(trace.endpos, rvec, trace.endpos);
|
|
VectorAdd(trace.endpos, tv(0,0,-256), dest);
|
|
trace = gi.trace(trace.endpos, VEC_ORIGIN, VEC_ORIGIN, dest, self, MASK_SOLID | MASK_WATER);
|
|
//lavacode
|
|
if ((trace.fraction < 1) && !(trace.contents & (CONTENTS_LAVA | CONTENTS_SLIME)))
|
|
{
|
|
VectorCopy(right, self->velocity);
|
|
|
|
self->velocity[2] = 300;
|
|
self->groundentity = NULL;
|
|
// self->s.origin[2] += 1;
|
|
|
|
gi.linkentity(self);
|
|
|
|
VectorCopy(self->velocity, self->jump_velocity);
|
|
|
|
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
|
|
}
|
|
else // jump straight up
|
|
{
|
|
VectorClear(self->velocity);
|
|
|
|
self->velocity[2] = 300;
|
|
self->groundentity = NULL;
|
|
// self->s.origin[2] += 1;
|
|
|
|
gi.linkentity(self);
|
|
|
|
VectorCopy(self->velocity, self->jump_velocity);
|
|
|
|
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (rnd >= 1) && (self->maxs[2] == 4)
|
|
&& (!self->goalentity || (self->goalentity->maxs[2] > 4))
|
|
&& (CanStand(self)))
|
|
{ // resume standing
|
|
self->maxs[2] = 32;
|
|
}
|
|
}
|
|
}
|
|
|
|
nojump:
|
|
|
|
if (self->groundentity && (strafe_speed > 0))
|
|
{
|
|
if (!M_walkmove(self,
|
|
self->s.angles[YAW] + (90 * ((self->strafe_dir * 2) - 1)),
|
|
strafe_speed * bot_frametime ))
|
|
{
|
|
self->strafe_dir = !self->strafe_dir;
|
|
self->strafe_changedir_time = level.time + 0.5 + (random() * 0.5);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
else // once we sight them again, don't fire instantaneously (super-human powers)
|
|
{
|
|
self->sight_enemy_time = level.time;
|
|
//FIXME ACRID SENTRY
|
|
// abort chasing a RL welding human, with enough health//Acrid sec
|
|
if (!self->enemy->bot_client && (self->enemy->client) &&
|
|
(self->enemy->client->pers.weapon == item_rocketlauncher) &&
|
|
!CarryingFlag(self->enemy)
|
|
&& ((self->enemy->health > 25)))//3/99 || (self->bot_fire == botBlaster || self->bot_fire == botShotgun)))
|
|
{ // abort the attack
|
|
// move away
|
|
if (self->goalentity)
|
|
{
|
|
self->goalentity->ignore_time = level.time + 1;
|
|
self->goalentity = NULL;
|
|
}
|
|
|
|
if (self->enemy->client->pers.weapon == item_rocketlauncher)
|
|
self->enemy->ignore_time = level.time + 2;
|
|
|
|
self->enemy = NULL;
|
|
}
|
|
}
|
|
}
|
|
/*****************************************
|
|
* Bot firing code *
|
|
* uses g_spawn g_local.h *
|
|
* bot_procs.h and bot_misc.c *
|
|
*****************************************/
|
|
void botBlaster (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
float dist, tf;
|
|
int damage;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
if ((self->enemy->health > 0) && (skill->value > 1) && (self->enemy->client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, dist * (1/1000), self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= 0.5 + (VectorLength(self->enemy->velocity)/600);
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
damage = 15;
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
monster_fire_blaster (self, start, forward, damage, 1000, MZ_BLASTER, EF_BLASTER);
|
|
|
|
}
|
|
|
|
//knife
|
|
void botKnife (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage = DAMAGE_KNIFE;
|
|
int kick = 4;
|
|
|
|
botDebugPrint("knife\n");
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
self->client->kick_angles[0] = -2;
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
// don't go more than 15 degrees up or down
|
|
if (abs(self->s.angles[PITCH]) > 15)
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *=4;
|
|
}
|
|
fire_stab (self, start, forward, damage, kick, MOD_KNIFE);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
}
|
|
void botMachineGun (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage;
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
|
|
damage = 3;
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
monster_fire_bullet (self, start, forward, damage, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1, MOD_MACHINEGUN);
|
|
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
/*
|
|
#ifdef _WIN32
|
|
_ftime(&self->lastattack_time);
|
|
#else
|
|
ftime(&self->lastattack_time);
|
|
#endif
|
|
*/
|
|
}
|
|
void botAk47 (edict_t *self)
|
|
{
|
|
int i;
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage = 13;
|
|
int kick = 3;
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
for (i=1 ; i<3 ; i++)
|
|
{
|
|
self->client->kick_origin[i] = crandom() * 0.35;
|
|
self->client->kick_angles[i] = crandom() * 0.7;
|
|
}
|
|
self->client->kick_origin[0] = crandom() * 0.35;
|
|
self->client->kick_angles[0] = self->client->machinegun_shots * -1.5;
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
fire_bullet (self, start, forward, damage, kick, 200, 300, MOD_AK47);
|
|
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
//send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_MACHINEGUN | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
}
|
|
void botPistol (edict_t *self)//WF34 FIXME
|
|
{
|
|
int i;
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage = 35;
|
|
int kick = 2;
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
for (i=1 ; i<3 ; i++)
|
|
{
|
|
self->client->kick_origin[i] = crandom() * 0.35;
|
|
self->client->kick_angles[i] = crandom() * 0.7;
|
|
}
|
|
self->client->kick_origin[0] = crandom() * 0.35;
|
|
self->client->kick_angles[0] = self->client->machinegun_shots * -1.5;
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/pistol.wav"), 1, ATTN_NORM, 0);//FIXME ACRID
|
|
fire_bullet (self, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD / 2, DEFAULT_BULLET_VSPREAD / 2, MOD_PISTOL);
|
|
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
}
|
|
void botShotgun (edict_t *self)//WF34 FIXME
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage;
|
|
float dist=0, tf;
|
|
botDebugPrint("shotgun\n");
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
damage = 3;
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
monster_fire_shotgun (self, start, forward, damage, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MZ_SHOTGUN, MOD_SHOTGUN);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
if (dist > 600)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
|
|
void botSuperShotgun (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t angles;
|
|
int damage;
|
|
float dist=0, tf;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
damage = 3*2; // OPTIMIZE: increase damage, decrease number of bullets
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
vectoangles(forward, angles);
|
|
|
|
angles[YAW] += 5;
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
monster_fire_shotgun (self, start, forward, damage, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, DEFAULT_SSHOTGUN_COUNT/4, MZ_SSHOTGUN, MOD_SSHOTGUN);
|
|
|
|
angles[YAW] -= 10;
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
monster_fire_shotgun (self, start, forward, damage, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, DEFAULT_SSHOTGUN_COUNT/4, MZ_SSHOTGUN | MZ_SILENCED, MOD_SSHOTGUN);
|
|
|
|
self->client->pers.inventory[self->client->ammo_index] -= 2;
|
|
if (self->client->pers.inventory[self->client->ammo_index] < 0)
|
|
self->client->pers.inventory[self->client->ammo_index] = 0;
|
|
|
|
if (dist > 600)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
|
|
void botChaingun (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int shots, damage, kick;
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
|
|
if (tf > 0)
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
|
|
if (self->client->killer_yaw < (level.time - 0.3))
|
|
{
|
|
if (self->client->killer_yaw < (level.time - 0.5))
|
|
{ // must have stopped firing, so need to restart wind-up
|
|
self->client->machinegun_shots = 0;
|
|
}
|
|
|
|
self->client->killer_yaw = level.time;
|
|
self->client->machinegun_shots++;
|
|
}
|
|
|
|
shots = self->client->machinegun_shots;
|
|
if (shots > 3)
|
|
shots = self->client->machinegun_shots = 3;
|
|
|
|
// optimize, simulate more shots by increasing the damage, but still only firing one shot
|
|
// chaingun is responsible for a LOT of cpu usage
|
|
damage = 3 * shots;
|
|
kick = 2;
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
// for (i=0 ; i<shots ; i++)
|
|
// {
|
|
// get start / end positions
|
|
fire_bullet (self, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD * 0.5, DEFAULT_BULLET_VSPREAD * 0.5, MOD_CHAINGUN);
|
|
// }
|
|
|
|
if ((self->client->pers.inventory[self->client->ammo_index] -= shots) < 0)
|
|
{
|
|
self->client->pers.inventory[self->client->ammo_index] = 0;
|
|
}
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte ((MZ_CHAINGUN1 + shots - 1));
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
/*
|
|
#ifdef _WIN32
|
|
_ftime(&self->lastattack_time);
|
|
#else
|
|
ftime(&self->lastattack_time);
|
|
#endif
|
|
*/
|
|
}
|
|
|
|
void botRailgun (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage, kick;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf=0;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
// if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf = (VectorLength(self->enemy->velocity) / 300) * 100;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
tf = 32;
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf += (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
}
|
|
|
|
if (tf > 0)
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
damage = 100;
|
|
kick = 200;
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_rail (self, start, forward, damage, kick, MOD_RAILGUN);//WF ADDED false
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_RAILGUN);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
}
|
|
|
|
void botRocketLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target, end_trace;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t oldorg, vel;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
float dist=0, tf;
|
|
trace_t trace;
|
|
|
|
// fire at peak of jump
|
|
if ((self->bot_stats->combat > 3) && !self->groundentity && (self->velocity[2] > 50))
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
damage = 100 + (int)(random() * 20.0);
|
|
radius_damage = 120;
|
|
damage_radius = 120;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorCopy(self->enemy->velocity, vel);
|
|
if (vel[2] > 0)
|
|
vel[2] = 0;
|
|
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, vel, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if (self->bot_stats->combat > 3)
|
|
{ // aim towards the ground?
|
|
trace_t tr;
|
|
|
|
VectorCopy(target, end_trace);
|
|
end_trace[2] -= 64;
|
|
tr = gi.trace(target, NULL, NULL, end_trace, self->enemy, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction < 1)
|
|
{
|
|
vec3_t end, org;
|
|
|
|
VectorCopy(tr.endpos, end);
|
|
|
|
VectorCopy(self->s.origin, org);
|
|
org[2] += self->viewheight;
|
|
|
|
tr = gi.trace(org, NULL, NULL, end, self, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction == 1)
|
|
{
|
|
VectorCopy(end, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// check to make sure the rocket won't explode in our face
|
|
//sentry ->client
|
|
if (!self->enemy->bot_client)// && self->enemy->client)//3/99 reversed
|
|
{
|
|
// move the enemy to the predicted position
|
|
VectorCopy(self->enemy->s.origin, oldorg);
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, self->enemy->velocity, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
VectorScale(forward, 130, end_trace);
|
|
VectorAdd(start, end_trace, end_trace);
|
|
trace = gi.trace(start, tv(-12,-12,-4), tv(12,12,4), end_trace, self, MASK_PLAYERSOLID);
|
|
//sentry ->client
|
|
if (!self->enemy->bot_client)// && self->enemy->client)//3/99 reversed
|
|
{
|
|
// move the enemy back to their correct position
|
|
VectorCopy(oldorg, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
if ( (trace.fraction < 1)
|
|
&& ( (self->health > 15)
|
|
|| (!trace.ent))) // dangerous situation, only fire if almost dead and obstacle is another player
|
|
{
|
|
// walk backwards
|
|
if (!M_walkmove(self, self->s.angles[YAW] + 180, BOT_RUN_SPEED * bot_frametime))
|
|
{
|
|
// FIXME: can't walk backwards, select a good close-range weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_rocket (self, start, forward, damage, 650, damage_radius, radius_damage, MOD_ROCKET);//WF ADDED
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 700)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
void botStingerRocketLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target, end_trace;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t oldorg, vel;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
float dist=0, tf;
|
|
trace_t trace;
|
|
|
|
// fire at peak of jump
|
|
if ((self->bot_stats->combat > 3) && !self->groundentity && (self->velocity[2] > 50))
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
damage = 100 + (int)(random() * 20.0);
|
|
radius_damage = 120;
|
|
damage_radius = 120;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorCopy(self->enemy->velocity, vel);
|
|
if (vel[2] > 0)
|
|
vel[2] = 0;
|
|
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, vel, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if (self->bot_stats->combat > 3)
|
|
{ // aim towards the ground?
|
|
trace_t tr;
|
|
|
|
VectorCopy(target, end_trace);
|
|
end_trace[2] -= 64;
|
|
tr = gi.trace(target, NULL, NULL, end_trace, self->enemy, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction < 1)
|
|
{
|
|
vec3_t end, org;
|
|
|
|
VectorCopy(tr.endpos, end);
|
|
|
|
VectorCopy(self->s.origin, org);
|
|
org[2] += self->viewheight;
|
|
|
|
tr = gi.trace(org, NULL, NULL, end, self, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction == 1)
|
|
{
|
|
VectorCopy(end, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// check to make sure the rocket won't explode in our face
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy to the predicted position
|
|
VectorCopy(self->enemy->s.origin, oldorg);
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, self->enemy->velocity, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
VectorScale(forward, 130, end_trace);
|
|
VectorAdd(start, end_trace, end_trace);
|
|
trace = gi.trace(start, tv(-12,-12,-4), tv(12,12,4), end_trace, self, MASK_PLAYERSOLID);
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy back to their correct position
|
|
VectorCopy(oldorg, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
if ( (trace.fraction < 1)
|
|
&& ( (self->health > 15)
|
|
|| (!trace.ent))) // dangerous situation, only fire if almost dead and obstacle is another player
|
|
{
|
|
// walk backwards
|
|
if (!M_walkmove(self, self->s.angles[YAW] + 180, BOT_RUN_SPEED * bot_frametime))
|
|
{
|
|
// FIXME: can't walk backwards, select a good close-range weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_stinger (self, start, forward, damage, 800, damage_radius, radius_damage);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 700)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
void botPelletRocketLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target, end_trace;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t oldorg, vel;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
float dist=0, tf;
|
|
trace_t trace;
|
|
|
|
// fire at peak of jump
|
|
if ((self->bot_stats->combat > 3) && !self->groundentity && (self->velocity[2] > 50))
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
damage = 100 + (int)(random() * 20.0);
|
|
radius_damage = 120;
|
|
damage_radius = 120;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorCopy(self->enemy->velocity, vel);
|
|
if (vel[2] > 0)
|
|
vel[2] = 0;
|
|
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, vel, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if (self->bot_stats->combat > 3)
|
|
{ // aim towards the ground?
|
|
trace_t tr;
|
|
|
|
VectorCopy(target, end_trace);
|
|
end_trace[2] -= 64;
|
|
tr = gi.trace(target, NULL, NULL, end_trace, self->enemy, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction < 1)
|
|
{
|
|
vec3_t end, org;
|
|
|
|
VectorCopy(tr.endpos, end);
|
|
|
|
VectorCopy(self->s.origin, org);
|
|
org[2] += self->viewheight;
|
|
|
|
tr = gi.trace(org, NULL, NULL, end, self, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction == 1)
|
|
{
|
|
VectorCopy(end, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// check to make sure the rocket won't explode in our face
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy to the predicted position
|
|
VectorCopy(self->enemy->s.origin, oldorg);
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, self->enemy->velocity, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
VectorScale(forward, 130, end_trace);
|
|
VectorAdd(start, end_trace, end_trace);
|
|
trace = gi.trace(start, tv(-12,-12,-4), tv(12,12,4), end_trace, self, MASK_PLAYERSOLID);
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy back to their correct position
|
|
VectorCopy(oldorg, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
if ( (trace.fraction < 1)
|
|
&& ( (self->health > 15)
|
|
|| (!trace.ent))) // dangerous situation, only fire if almost dead and obstacle is another player
|
|
{
|
|
// walk backwards
|
|
if (!M_walkmove(self, self->s.angles[YAW] + 180, BOT_RUN_SPEED * bot_frametime))
|
|
{
|
|
// FIXME: can't walk backwards, select a good close-range weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_pellet_rocket (self, start, forward, damage, 650, damage_radius, radius_damage);//WF ADDED
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 600)//FIXME > FAR
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
|
|
}
|
|
void botRocketNapalmLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target, end_trace;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t oldorg, vel;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
float dist=0, tf;
|
|
trace_t trace;
|
|
|
|
// fire at peak of jump
|
|
if ((self->bot_stats->combat > 3) && !self->groundentity && (self->velocity[2] > 50))
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
damage = 100 + (int)(random() * 20.0);
|
|
radius_damage = 120;
|
|
damage_radius = 120;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorCopy(self->enemy->velocity, vel);
|
|
if (vel[2] > 0)
|
|
vel[2] = 0;
|
|
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, vel, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if (self->bot_stats->combat > 3)
|
|
{ // aim towards the ground?
|
|
trace_t tr;
|
|
|
|
VectorCopy(target, end_trace);
|
|
end_trace[2] -= 64;
|
|
tr = gi.trace(target, NULL, NULL, end_trace, self->enemy, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction < 1)
|
|
{
|
|
vec3_t end, org;
|
|
|
|
VectorCopy(tr.endpos, end);
|
|
|
|
VectorCopy(self->s.origin, org);
|
|
org[2] += self->viewheight;
|
|
|
|
tr = gi.trace(org, NULL, NULL, end, self, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction == 1)
|
|
{
|
|
VectorCopy(end, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// check to make sure the rocket won't explode in our face
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy to the predicted position
|
|
VectorCopy(self->enemy->s.origin, oldorg);
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, self->enemy->velocity, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
VectorScale(forward, 130, end_trace);
|
|
VectorAdd(start, end_trace, end_trace);
|
|
trace = gi.trace(start, tv(-12,-12,-4), tv(12,12,4), end_trace, self, MASK_PLAYERSOLID);
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy back to their correct position
|
|
VectorCopy(oldorg, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
if ( (trace.fraction < 1)
|
|
&& ( (self->health > 15)
|
|
|| (!trace.ent))) // dangerous situation, only fire if almost dead and obstacle is another player
|
|
{
|
|
// walk backwards
|
|
if (!M_walkmove(self, self->s.angles[YAW] + 180, BOT_RUN_SPEED * bot_frametime))
|
|
{
|
|
// FIXME: can't walk backwards, select a good close-range weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_napalmrocket (self, start, forward, damage, 650, damage_radius, radius_damage);//WF ADDED
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 700)//FIXME > FAR 700
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
|
|
}
|
|
void botRocketClusterLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target, end_trace;
|
|
vec3_t forward, right, ofs;
|
|
vec3_t oldorg, vel;
|
|
int damage;
|
|
float damage_radius;
|
|
int radius_damage;
|
|
float dist=0, tf;
|
|
trace_t trace;
|
|
|
|
// fire at peak of jump
|
|
if ((self->bot_stats->combat > 3) && !self->groundentity && (self->velocity[2] > 50))
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
damage = 100 + (int)(random() * 20.0);
|
|
radius_damage = 120;
|
|
damage_radius = 120;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorCopy(self->enemy->velocity, vel);
|
|
if (vel[2] > 0)
|
|
vel[2] = 0;
|
|
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, vel, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if (self->bot_stats->combat > 3)
|
|
{ // aim towards the ground?
|
|
trace_t tr;
|
|
|
|
VectorCopy(target, end_trace);
|
|
end_trace[2] -= 64;
|
|
tr = gi.trace(target, NULL, NULL, end_trace, self->enemy, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction < 1)
|
|
{
|
|
vec3_t end, org;
|
|
|
|
VectorCopy(tr.endpos, end);
|
|
|
|
VectorCopy(self->s.origin, org);
|
|
org[2] += self->viewheight;
|
|
|
|
tr = gi.trace(org, NULL, NULL, end, self, CONTENTS_SOLID);
|
|
|
|
if (tr.fraction == 1)
|
|
{
|
|
VectorCopy(end, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// check to make sure the rocket won't explode in our face
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy to the predicted position
|
|
VectorCopy(self->enemy->s.origin, oldorg);
|
|
VectorMA (self->enemy->s.origin, (float) dist / 650, self->enemy->velocity, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
VectorScale(forward, 130, end_trace);
|
|
VectorAdd(start, end_trace, end_trace);
|
|
trace = gi.trace(start, tv(-12,-12,-4), tv(12,12,4), end_trace, self, MASK_PLAYERSOLID);
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
{
|
|
// move the enemy back to their correct position
|
|
VectorCopy(oldorg, self->enemy->s.origin);
|
|
gi.linkentity(self->enemy);
|
|
}
|
|
|
|
if ( (trace.fraction < 1)
|
|
&& ( (self->health > 15)
|
|
|| (!trace.ent))) // dangerous situation, only fire if almost dead and obstacle is another player
|
|
{
|
|
// walk backwards
|
|
if (!M_walkmove(self, self->s.angles[YAW] + 180, BOT_RUN_SPEED * bot_frametime))
|
|
{
|
|
// FIXME: can't walk backwards, select a good close-range weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_clusterrocket (self, start, forward, damage, 650, damage_radius, radius_damage);//WF ADDED
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist < 600)//FIXME FAR > 700
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
|
|
}
|
|
void botGrenadeLauncher (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs, angles;
|
|
int damage;
|
|
int radius;
|
|
float dist=0, tf;
|
|
|
|
damage = 120;
|
|
radius = damage + 40;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{//sentry ->client
|
|
if ((self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, dist / 550, self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
vectoangles(forward, angles);
|
|
|
|
// angle upwards a bit
|
|
angles[PITCH] -= 15 * ((dist < 384) ? ((dist / 384) * 2) - 1: 1);
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
fire_grenade (self, start, forward, damage, 600, 2.5, radius);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_GRENADE);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 700)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
else if (dist < radius)
|
|
{
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
}
|
|
#define GRENADE_TIMER 3.0
|
|
#define GRENADE_MINSPEED 400
|
|
#define GRENADE_MAXSPEED 800
|
|
|
|
void botGrenades (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs, angles;
|
|
int damage;
|
|
int radius;
|
|
float dist=0, tf;
|
|
// qboolean held;
|
|
int lbdamage = 15; //reduce damage
|
|
float timer;
|
|
int speed;
|
|
damage = 125;
|
|
radius = damage + 40;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
dist = entdist(self, self->enemy);//fixme????
|
|
timer = self->client->grenade_time - level.time;
|
|
speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER);
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{//sentry ->client
|
|
if ((self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, dist / 550, self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
vectoangles(forward, angles);
|
|
|
|
// angle upwards a bit
|
|
angles[PITCH] -= 15 * ((dist < 384) ? ((dist / 384) * 2) - 1: 1);
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
fire_grenade2 (self, start, forward, damage, 600, 2.5, radius, false);//fixme held
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
if (dist > 700)
|
|
{ // check for a better long distance weapon
|
|
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
else if (dist < radius)
|
|
{
|
|
botPickBestCloseWeapon(self);
|
|
}
|
|
self->client->grenade_time = level.time + 1.0;
|
|
}//TEST ACRID FIXME GRENADES
|
|
void botHyperblaster (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
float dist=0, tf;
|
|
int damage, effect;
|
|
|
|
damage = 15;
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, dist/1000, self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (VectorLength(self->enemy->velocity)/600);
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
|
|
if ((random() * 3) < 1)
|
|
effect = EF_HYPERBLASTER;
|
|
else
|
|
effect = 0;
|
|
|
|
fire_blaster (self, start, forward, damage, 1000, effect, true);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_HYPERBLASTER);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
}
|
|
|
|
void botBFG (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage;
|
|
float damage_radius;
|
|
float dist=0, tf;
|
|
|
|
damage = 500;
|
|
damage_radius = 1000;
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
//sentry ->client
|
|
if ((self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, entdist(self, self->enemy) * (1/550), self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if ((dist > 200) && self->enemy->groundentity) // aim towards ground
|
|
target[2] -= (4 * self->bot_stats->combat);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
fire_bfg (self, start, forward, damage, 400, damage_radius);
|
|
if ((self->client->pers.inventory[self->client->ammo_index] -= 60) < 0)
|
|
self->client->pers.inventory[self->client->ammo_index] = 0;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_BFG);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 1000)
|
|
{ // check for a better long distance weapon
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
//ACRID START WF WEAPONS
|
|
void botPulseCannon (edict_t *self)
|
|
{
|
|
//fixme int i;
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;// , up;
|
|
//fixme float r, u;
|
|
int shots, damage, kick = 8;
|
|
|
|
if (deathmatch->value)
|
|
damage = 3;
|
|
else
|
|
damage = 6;
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
|
|
if (tf > 0)
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
|
|
if (self->client->killer_yaw < (level.time - 0.3))
|
|
{
|
|
if (self->client->killer_yaw < (level.time - 0.5))
|
|
{ // must have stopped firing, so need to restart wind-up
|
|
self->client->machinegun_shots = 0;
|
|
}
|
|
|
|
self->client->killer_yaw = level.time;
|
|
self->client->machinegun_shots++;
|
|
}
|
|
|
|
shots = self->client->machinegun_shots;
|
|
if (shots > 3)
|
|
shots = self->client->machinegun_shots = 3;
|
|
|
|
// optimize, simulate more shots by increasing the damage, but still only firing one shot
|
|
// chaingun is responsible for a LOT of cpu usage
|
|
damage = 3 * shots;
|
|
kick = 2;
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
self->ShotNumber++;
|
|
if (self->client->ping >500)
|
|
{
|
|
if (self->ShotNumber>3)
|
|
{
|
|
fire_pulse (self, start, forward, damage*4, kick*4, 0, 250, 250, MOD_MBPC);
|
|
self->ShotNumber =0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (self->ShotNumber>1)
|
|
{
|
|
fire_pulse (self, start, forward, damage*2, kick*2, 0, 250, 250, MOD_MBPC);
|
|
self->ShotNumber =0;
|
|
}
|
|
}
|
|
|
|
if ((self->client->pers.inventory[self->client->ammo_index] -= shots) < 0)
|
|
{
|
|
self->client->pers.inventory[self->client->ammo_index] = 0;
|
|
}
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte ((MZ_CHAINGUN1 + shots - 1) | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
/*
|
|
#ifdef _WIN32
|
|
_ftime(&self->lastattack_time);
|
|
#else
|
|
ftime(&self->lastattack_time);
|
|
#endif
|
|
*/
|
|
}//NEEDLER
|
|
void botNeedler (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage, kick;
|
|
int xspread;
|
|
int yspread;
|
|
|
|
|
|
|
|
if ((int)(level.time*10) & 1) // only calculate every other frame
|
|
{
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
self->client->kick_angles[0] = -2;
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
}
|
|
|
|
VectorCopy(forward, self->last_forward);
|
|
VectorCopy(start, self->last_start);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(self->last_forward, forward);
|
|
VectorCopy(self->last_start, start);
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
damage = 3 + (((int)(random()*1000)) % 5);
|
|
xspread = 125;
|
|
yspread = 125;
|
|
kick = 2 + (((int)(random()*1000)) % 8);
|
|
}
|
|
else
|
|
{
|
|
damage = 4 + (((int)(random()*1000)) % 5);
|
|
xspread = 325;
|
|
yspread = 375;
|
|
kick = 3 + (((int)(random()*1000)) % 6);
|
|
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
xspread *=2.5;
|
|
yspread *=2.5;
|
|
kick *=4;
|
|
}
|
|
|
|
fire_needle(self, start, forward, damage, kick, TE_BLASTER, xspread, yspread);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
}//FLAME THROWER
|
|
void botFlameThrower (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage = 5;
|
|
float damage_radius = 1000;
|
|
float dist=0, tf;
|
|
|
|
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
//sentry ->client
|
|
if ((self->enemy->health > 0) && (self->enemy->client && !self->enemy->bot_client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, entdist(self, self->enemy) * (1/550), self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
|
|
if ((dist > 200) && self->enemy->groundentity) // aim towards ground
|
|
target[2] -= (4 * self->bot_stats->combat);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
fire_flamethrower (self, start, forward, damage*3, 600, damage_radius*2.5);
|
|
self->client->pers.inventory[self->client->ammo_index]--;//find all these fixme acrid
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_ROCKET | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
if (dist > 700)//FIXME 1000
|
|
{ // check for a better long distance weapon
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
}
|
|
//PoisonDart//ArmorDart
|
|
void botPoisonDart (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage;
|
|
float dist=0, tf;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
damage = 20;
|
|
}
|
|
else
|
|
{
|
|
damage = 25;
|
|
}
|
|
if (is_quad)
|
|
damage *= 4;
|
|
//confusing someone coded a poisondart and armor dart???
|
|
// fire_poisondart (self, start, forward, damage, 750, EF_GREENGIB);
|
|
fire_armordart (self, start, forward, damage, 750, EF_GREENGIB);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
if (dist > 600)
|
|
{ // check for a better long distance weapon
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
}
|
|
void botTeslaCoil (edict_t *self)//fixme acrid
|
|
{
|
|
vec3_t forward, right ,start, target;
|
|
vec3_t ofs;
|
|
float dist=0, tf;
|
|
//fixme trace_t tr;
|
|
int damage, kick;
|
|
int xspread;
|
|
int yspread;
|
|
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
damage = 3 + (((int)(random()*1000)) % 4);
|
|
xspread = 25;
|
|
yspread = 25;
|
|
kick = 2 + (((int)(random()*1000)) % 8);
|
|
}
|
|
else
|
|
{
|
|
damage = 6 + (((int)(random()*1000)) % 4);
|
|
xspread = 75;
|
|
yspread = 75;
|
|
kick = 3 + (((int)(random()*1000)) % 6);
|
|
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
xspread *=2.5;
|
|
yspread *=2.5;
|
|
kick *=4;
|
|
}
|
|
AngleVectors (self->client->v_angle, forward, right, NULL);//WF
|
|
// AngleVectors (self->s.angles, forward, right, NULL);//ERASER
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
VectorScale(forward, 8, ofs);
|
|
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
// start[2] += self->viewheight - 8;//ERASER
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
dist = entdist(self, self->enemy);
|
|
//sentry ->client
|
|
if ((skill->value > 1) && (self->enemy->health > 0) && (self->enemy->client) && (dist > 64))
|
|
{
|
|
VectorMA (self->enemy->s.origin, dist/1000, self->enemy->velocity, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (VectorLength(self->enemy->velocity)/600);
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
// VectorCopy (self->enemy->s.origin, end);
|
|
|
|
fire_telsa(self, start, forward, damage, kick, TE_BLASTER, xspread, yspread);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
|
|
}
|
|
|
|
void botInfectedDart (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage, speed;
|
|
float dist=0, tf;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
damage = 20;
|
|
}
|
|
else
|
|
{
|
|
damage = 25;
|
|
}
|
|
if (is_quad)
|
|
damage *= 4;
|
|
speed = 180; //was 850
|
|
fire_infecteddart (self, start, forward, damage, speed, EF_GIB);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
if (dist > 600)//fixme acrid nurse
|
|
{ // check for a better long distance weapon
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
}
|
|
void botLightningGun (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage, kick, speed;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
float dist, tf=0;
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
// if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
//sentry ->client
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf = (VectorLength(self->enemy->velocity) / 300) * 100;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
tf = 32;
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf += (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
}
|
|
|
|
if (tf > 0)
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.1), target);
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
if (deathmatch->value)
|
|
{ // normal damage is too extreme in dm
|
|
speed = 880; //Slow it down a little
|
|
damage = 100 + (((int)(random()*1000)) % 30);
|
|
kick = 165+ (((int)(random()*1000)) % 30) ;
|
|
}
|
|
else
|
|
{
|
|
speed = 960;
|
|
damage = 150 + (((int)(random()*1000)) % 55);
|
|
kick = 175 + (((int)(random()*1000)) % 55);
|
|
}
|
|
|
|
|
|
if (is_quad)
|
|
damage *= 4;
|
|
|
|
fire_lightning (self, start, forward, damage, speed, kick);//fixme, false);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_RAILGUN | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
|
|
}
|
|
void botSHC (edict_t *self)
|
|
{
|
|
vec3_t start, target;
|
|
vec3_t forward, right, ofs;
|
|
int damage = 3;
|
|
int kick = 8;
|
|
float dist=0, tf;
|
|
|
|
AngleVectors (self->s.angles, forward, right, NULL);
|
|
G_ProjectSource (self->s.origin, tv(8,8,self->viewheight-8), forward, right, start);
|
|
|
|
VectorScale(forward, 8, ofs);
|
|
VectorAdd(self->s.origin, ofs, start);
|
|
start[2] += self->viewheight - 8;
|
|
|
|
if (self->enemy && infront(self, self->enemy))
|
|
{
|
|
|
|
dist = entdist(self, self->enemy);
|
|
|
|
if (self->enemy->health > 0)
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
|
|
if (skill->value <= 1)
|
|
{ // trail the player's velocity
|
|
VectorMA(target, -0.2, self->enemy->velocity, target);
|
|
}
|
|
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (self->enemy->s.origin, target);
|
|
target[2] += self->enemy->viewheight - 8;
|
|
}
|
|
|
|
if (self->bot_stats->accuracy < 5)
|
|
{//sentry ->client
|
|
tf = (dist < 256) ? dist/2 : 256;
|
|
tf *= (float) ((5.0 - self->bot_stats->accuracy) / 5.0) * 2;
|
|
if (self->enemy->client && !self->enemy->bot_client)
|
|
tf *= (1 - (VectorLength(self->enemy->velocity)/600));
|
|
VectorAdd(target, tv(crandom() * tf, crandom() * tf, crandom() * tf * 0.2), target);
|
|
}
|
|
|
|
VectorSubtract (target, start, forward);
|
|
VectorNormalize (forward);
|
|
|
|
vectoangles(forward, self->s.angles);
|
|
if (abs(self->s.angles[PITCH]) > 15) // don't go more than 15 degrees up or down
|
|
self->s.angles[PITCH] = (((self->s.angles[PITCH] > 0) * 2) - 1) * 15;
|
|
}
|
|
else
|
|
{
|
|
aborted_fire = true;
|
|
return;
|
|
// AngleVectors (self->s.angles, forward, NULL, NULL);
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= 4;
|
|
kick *= 4;
|
|
}
|
|
|
|
if (deathmatch->value)
|
|
fire_shc (self, start, forward, damage, kick,TE_GUNSHOT, 500, 500);
|
|
else
|
|
fire_shc (self, start, forward, damage, kick,TE_GUNSHOT, 500, 500);
|
|
self->client->pers.inventory[self->client->ammo_index]--;
|
|
|
|
if (dist > 700)
|
|
{ // check for a better long distance weapon
|
|
botPickBestFarWeapon(self);
|
|
}
|
|
// send muzzle flash
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (self-g_edicts);
|
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
|
}
|
|
//ACRID END
|
|
/**********************/
|
|
/* BOT BEST,CLOSE,FAR */
|
|
/**********************/
|
|
////////////////////////////////////////////////////////
|
|
// Returns the ammo index for the given new weapon item/
|
|
////////////////////////////////////////////////////////
|
|
int botAmmoIndex(gitem_t *weapon) {
|
|
//Shell Weapons
|
|
if (weapon==item_shotgun) return ITEM_INDEX(item_shells);
|
|
if (weapon==item_supershotgun) return ITEM_INDEX(item_shells);
|
|
if (weapon==item_infecteddart) return ITEM_INDEX(item_shells);
|
|
if (weapon==item_poisondart) return ITEM_INDEX(item_shells);
|
|
if (weapon==item_shc) return ITEM_INDEX(item_shells);
|
|
//3/99 if (weapon==item_knife) return ITEM_INDEX(item_shells);
|
|
//Bullet Weapons
|
|
if (weapon==item_pistol) return ITEM_INDEX(item_bullets);
|
|
if (weapon==item_needler) return ITEM_INDEX(item_bullets);
|
|
if (weapon==item_machinegun) return ITEM_INDEX(item_bullets);
|
|
if (weapon==item_chaingun) return ITEM_INDEX(item_bullets);
|
|
if (weapon==item_ak47) return ITEM_INDEX(item_bullets);
|
|
if (weapon==item_pulsecannon) return ITEM_INDEX(item_bullets);
|
|
//Rocket Weapons
|
|
if (weapon==item_rocketlauncher) return ITEM_INDEX(item_rockets);
|
|
if (weapon==item_rocketnapalmlauncher) return ITEM_INDEX(item_rockets);
|
|
if (weapon==item_pelletrocketlauncher) return ITEM_INDEX(item_rockets);
|
|
if (weapon==item_rocketclusterlauncher) return ITEM_INDEX(item_rockets);
|
|
if (weapon==item_stingerrocketlauncher) return ITEM_INDEX(item_rockets);
|
|
//Slug Weapons
|
|
if (weapon==item_railgun) return ITEM_INDEX(item_slugs);
|
|
if (weapon==item_lightninggun) return ITEM_INDEX(item_slugs);
|
|
if (weapon==item_sniperrifle) return ITEM_INDEX(item_slugs);
|
|
//Cell Weapons
|
|
if (weapon==item_hyperblaster) return ITEM_INDEX(item_cells);
|
|
if (weapon==item_bfg10k) return ITEM_INDEX(item_cells);
|
|
if (weapon==item_flamethrower) return ITEM_INDEX(item_cells);
|
|
if (weapon==item_telsacoil) return ITEM_INDEX(item_cells);
|
|
//Grenade Weapons
|
|
if (weapon==item_handgrenades) return ITEM_INDEX(item_grenades);
|
|
if (weapon==item_grenadelauncher) return ITEM_INDEX(item_grenades);
|
|
|
|
return 0; // Else blaster
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Check inventory ammo against new weapon and ammo specials like homing cells/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
qboolean botHasAmmoForWeapon(edict_t *self, gitem_t *weapon) {
|
|
|
|
if ((int)dmflags->value & DF_INFINITE_AMMO)
|
|
return true;
|
|
|
|
if (weapon==item_blaster)
|
|
return true;
|
|
|
|
if (weapon == item_knife)//3/99
|
|
return true;
|
|
|
|
if (weapon==item_bfg10k)
|
|
return (self->client->pers.inventory[botAmmoIndex(item_bfg10k)]>=50);
|
|
|
|
if (weapon==item_supershotgun)
|
|
return (self->client->pers.inventory[botAmmoIndex(item_supershotgun)]>=2);
|
|
|
|
return (self->client->pers.inventory[botAmmoIndex(weapon)]);
|
|
}
|
|
/////////////////////////////////////////
|
|
// Return a weapon in the bots inventory/
|
|
/////////////////////////////////////////
|
|
qboolean botHasWeaponInInventory(edict_t *self, gitem_t *item) {
|
|
return (self->client->pers.inventory[ITEM_INDEX(item)]);
|
|
}
|
|
///////////////////////////////////////
|
|
// Returns true if bot has that weapon/
|
|
///////////////////////////////////////
|
|
qboolean botHasThisWeapon(edict_t *self, gitem_t *weapon)
|
|
{
|
|
gclient_t *client;
|
|
gitem_t *oldweapon;//test
|
|
client = self->client;
|
|
|
|
oldweapon = client->pers.weapon;//test
|
|
|
|
|
|
if (botHasWeaponInInventory(self,weapon))
|
|
if (botHasAmmoForWeapon(self,weapon)) {
|
|
client->newweapon = weapon;
|
|
|
|
// already using this weapon
|
|
if (client->pers.weapon == client->newweapon)
|
|
return true;
|
|
|
|
GetBotFireForWeapon(self->client->newweapon, &self->bot_fire);
|
|
self->last_fire = level.time + BOT_CHANGEWEAPON_DELAY;
|
|
|
|
if (client->newweapon == item_blaster)
|
|
self->fire_interval = FIRE_INTERVAL_BLASTER;
|
|
if (client->newweapon == item_shotgun)
|
|
self->fire_interval = FIRE_INTERVAL_SHOTGUN;
|
|
else if (client->newweapon == item_supershotgun)
|
|
self->fire_interval = FIRE_INTERVAL_SSHOTGUN;
|
|
else if (client->newweapon == item_rocketlauncher)
|
|
self->fire_interval = FIRE_INTERVAL_ROCKETLAUNCHER;
|
|
else if (client->newweapon == item_grenadelauncher)
|
|
self->fire_interval = FIRE_INTERVAL_GRENADELAUNCHER;
|
|
else if (client->newweapon == item_railgun)
|
|
self->fire_interval = FIRE_INTERVAL_RAILGUN;
|
|
else if (client->newweapon == item_hyperblaster)
|
|
self->fire_interval = FIRE_INTERVAL_HYPERBLASTER;
|
|
else if (client->newweapon == item_chaingun)
|
|
self->fire_interval = FIRE_INTERVAL_CHAINGUN;
|
|
else if (client->newweapon == item_machinegun)
|
|
self->fire_interval = FIRE_INTERVAL_MACHINEGUN;
|
|
else if (client->newweapon == item_bfg10k)
|
|
self->fire_interval = FIRE_INTERVAL_BFG;
|
|
//ACRID NEW WF STUFF
|
|
else if (client->newweapon == item_sniperrifle)
|
|
self->fire_interval =FIRE_INTERVAL_SNIPERRIFLE;
|
|
else if (client->newweapon == item_lightninggun)
|
|
self->fire_interval =FIRE_INTERVAL_LIGHTNINGGUN;
|
|
else if (client->newweapon == item_infecteddart)
|
|
self->fire_interval =FIRE_INTERVAL_INFECTEDDART;
|
|
else if (client->newweapon == item_pulsecannon)
|
|
self->fire_interval =FIRE_INTERVAL_PULSECANNON;
|
|
else if (client->newweapon == item_telsacoil)
|
|
self->fire_interval =FIRE_INTERVAL_TELSACOIL;
|
|
else if (client->newweapon == item_telsacoil)
|
|
self->fire_interval =FIRE_INTERVAL_FLAMETHROWER;
|
|
else if (client->newweapon == item_pelletrocketlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_PELLETROCKETLAUNCHER;
|
|
else if (client->newweapon == item_rocketnapalmlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_ROCKETNAPALMLAUNCHER;
|
|
else if (client->newweapon == item_rocketclusterlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_ROCKETCLUSTERLAUNCHER;
|
|
else if (client->newweapon == item_stingerrocketlauncher)
|
|
self->fire_interval = FIRE_INTERVAL_STINGERROCKETLAUNCHER;
|
|
else if (client->newweapon == item_needler)
|
|
self->fire_interval =FIRE_INTERVAL_NEEDLER;
|
|
else if (client->newweapon == item_shc)
|
|
self->fire_interval =FIRE_INTERVAL_SHC;
|
|
else if (client->newweapon == item_handgrenades)
|
|
self->fire_interval =FIRE_INTERVAL_GRENADES;
|
|
else if (client->newweapon == item_poisondart)
|
|
self->fire_interval =FIRE_INTERVAL_POISONDART;
|
|
else if (client->newweapon == item_ak47)
|
|
self->fire_interval =FIRE_INTERVAL_AK47;
|
|
else if (client->newweapon == item_pistol)
|
|
self->fire_interval =FIRE_INTERVAL_PISTOL;
|
|
else if (client->newweapon == item_knife)
|
|
self->fire_interval =FIRE_INTERVAL_KNIFE;
|
|
|
|
if (CTFApplyHaste(self))
|
|
self->fire_interval *= 0.5;
|
|
|
|
self->client->ammo_index=botAmmoIndex(weapon);
|
|
client->pers.weapon = client->newweapon;
|
|
self->client->pers.weapon = client->newweapon;
|
|
|
|
// set visible model vwep with 3.20 code
|
|
if (oldweapon != client->pers.weapon)
|
|
ShowGun(self);
|
|
|
|
|
|
return true; }
|
|
|
|
return false;
|
|
}
|
|
/*
|
|
===================
|
|
botPickBestWeapon
|
|
|
|
called everytime a weapon/ammo is picked up, or ammo runs out
|
|
===================
|
|
*/
|
|
void botPickBestWeapon(edict_t *self)
|
|
{
|
|
gclient_t *client;
|
|
gitem_t *oldweapon;
|
|
client = self->client;
|
|
|
|
oldweapon = client->pers.weapon;
|
|
// check favourite weapon
|
|
// if ( client->pers.inventory[self->bot_stats->fav_weapon->tag]
|
|
// && //acrid added if (
|
|
//NURSE Acrid FIXME
|
|
/* if ((self->enemy->client) && (self->enemy->disease) &&
|
|
SameTeam(self->enemy, self)&&
|
|
(self->client->player_class == 2)) &&
|
|
(self->client->pers.inventory[ITEM_INDEX(item_shells)] > 0))//crashing here
|
|
{botDebugPrint("Nurse testing 2\n");
|
|
goto NCure;
|
|
}*/
|
|
if ((self->client->pers.weapon == item_knife) &&
|
|
(CarryingFlag(self)))
|
|
{
|
|
botDebugPrint("Best knife for other (ACRID)\n");
|
|
goto skip;
|
|
}
|
|
//new check favourite weapon// fav not knife 3/99
|
|
if ((client->pers.inventory[ITEM_INDEX(self->bot_stats->fav_weapon)]&&
|
|
self->bot_stats->fav_weapon != item_knife &&
|
|
client->pers.inventory[ITEM_INDEX(FindItem(self->bot_stats->fav_weapon->ammo))])
|
|
//or fav is knife3/99
|
|
||(client->pers.inventory[ITEM_INDEX(self->bot_stats->fav_weapon)] &&
|
|
self->bot_stats->fav_weapon == item_knife))
|
|
{
|
|
// botDebugPrint("%s picked favourite weapon\n", self->client->pers.netname);
|
|
client->newweapon = self->bot_stats->fav_weapon;
|
|
|
|
if (client->pers.weapon == client->newweapon)
|
|
return; // already using this weapon
|
|
|
|
GetBotFireForWeapon(self->bot_stats->fav_weapon, &self->bot_fire);
|
|
|
|
self->last_fire = level.time + BOT_CHANGEWEAPON_DELAY;
|
|
|
|
if (client->newweapon == item_shotgun)
|
|
self->fire_interval = FIRE_INTERVAL_SHOTGUN;
|
|
else if (client->newweapon == item_supershotgun)
|
|
self->fire_interval = FIRE_INTERVAL_SSHOTGUN;
|
|
else if (client->newweapon == item_rocketlauncher)
|
|
self->fire_interval = FIRE_INTERVAL_ROCKETLAUNCHER;
|
|
else if (client->newweapon == item_grenadelauncher)
|
|
self->fire_interval = FIRE_INTERVAL_GRENADELAUNCHER;
|
|
else if (client->newweapon == item_railgun)
|
|
self->fire_interval = FIRE_INTERVAL_RAILGUN;
|
|
else if (client->newweapon == item_hyperblaster)
|
|
self->fire_interval = FIRE_INTERVAL_HYPERBLASTER;
|
|
else if (client->newweapon == item_chaingun)
|
|
self->fire_interval = FIRE_INTERVAL_CHAINGUN;
|
|
else if (client->newweapon == item_machinegun)
|
|
self->fire_interval = FIRE_INTERVAL_MACHINEGUN;
|
|
else if (client->newweapon == item_bfg10k)
|
|
self->fire_interval = FIRE_INTERVAL_BFG;
|
|
//ACRID NEW WF STUFF
|
|
else if (client->newweapon == item_sniperrifle)
|
|
self->fire_interval =FIRE_INTERVAL_SNIPERRIFLE;
|
|
else if (client->newweapon == item_lightninggun)
|
|
self->fire_interval =FIRE_INTERVAL_LIGHTNINGGUN;
|
|
else if (client->newweapon == item_infecteddart)
|
|
self->fire_interval =FIRE_INTERVAL_INFECTEDDART;
|
|
else if (client->newweapon == item_pulsecannon)
|
|
self->fire_interval =FIRE_INTERVAL_PULSECANNON;
|
|
else if (client->newweapon == item_telsacoil)
|
|
self->fire_interval =FIRE_INTERVAL_TELSACOIL;
|
|
else if (client->newweapon == item_telsacoil)
|
|
self->fire_interval =FIRE_INTERVAL_FLAMETHROWER;
|
|
else if (client->newweapon == item_pelletrocketlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_PELLETROCKETLAUNCHER;
|
|
else if (client->newweapon == item_rocketnapalmlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_ROCKETNAPALMLAUNCHER;
|
|
else if (client->newweapon == item_rocketclusterlauncher)
|
|
self->fire_interval =FIRE_INTERVAL_ROCKETCLUSTERLAUNCHER;
|
|
else if (client->newweapon == item_stingerrocketlauncher)
|
|
self->fire_interval = FIRE_INTERVAL_STINGERROCKETLAUNCHER;
|
|
else if (client->newweapon == item_needler)
|
|
self->fire_interval =FIRE_INTERVAL_NEEDLER;
|
|
else if (client->newweapon == item_shc)
|
|
self->fire_interval =FIRE_INTERVAL_SHC;
|
|
else if (client->newweapon == item_handgrenades)
|
|
self->fire_interval =FIRE_INTERVAL_GRENADES;
|
|
else if (client->newweapon == item_poisondart)
|
|
self->fire_interval =FIRE_INTERVAL_POISONDART;
|
|
else if (client->newweapon == item_ak47)
|
|
self->fire_interval =FIRE_INTERVAL_AK47;
|
|
else if (client->newweapon == item_pistol)
|
|
self->fire_interval =FIRE_INTERVAL_PISTOL;
|
|
else if (client->newweapon == item_knife)
|
|
self->fire_interval =FIRE_INTERVAL_KNIFE;
|
|
//ACRID NEW WF STUFF
|
|
|
|
if (CTFApplyHaste(self))
|
|
{
|
|
self->fire_interval *= 0.5;
|
|
}
|
|
|
|
// client->ammo_index = self->bot_stats->fav_weapon->tag;
|
|
if (client->newweapon != item_knife)//3/99
|
|
client->ammo_index = ITEM_INDEX(FindItem(self->bot_stats->fav_weapon->ammo));
|
|
client->pers.weapon = client->newweapon;
|
|
self->client->pers.weapon = client->newweapon;
|
|
goto found;
|
|
}
|
|
skip:
|
|
if (botHasThisWeapon(self,item_bfg10k)) return;
|
|
if (botHasThisWeapon(self,item_pulsecannon)) return;
|
|
if (botHasThisWeapon(self,item_needler)) return;
|
|
if (botHasThisWeapon(self,item_flamethrower)) return;
|
|
if (botHasThisWeapon(self,item_lightninggun)) return;
|
|
if (botHasThisWeapon(self,item_infecteddart)) return;
|
|
if (botHasThisWeapon(self,item_pelletrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_rocketnapalmlauncher)) return;
|
|
if (botHasThisWeapon(self,item_rocketclusterlauncher)) return;
|
|
if (botHasThisWeapon(self,item_shc)) return;
|
|
if (botHasThisWeapon(self,item_telsacoil)) return;
|
|
if (botHasThisWeapon(self,item_ak47)) return;
|
|
if (botHasThisWeapon(self,item_poisondart)) return;
|
|
if (botHasThisWeapon(self,item_pistol)) return;
|
|
if (botHasThisWeapon(self,item_railgun)) return;
|
|
if (botHasThisWeapon(self,item_hyperblaster)) return;
|
|
if (botHasThisWeapon(self,item_rocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_chaingun)) return;
|
|
if (botHasThisWeapon(self,item_supershotgun)) return;
|
|
if (botHasThisWeapon(self,item_grenadelauncher))return;
|
|
if (botHasThisWeapon(self,item_machinegun)) return;
|
|
if (botHasThisWeapon(self,item_stingerrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_shotgun)) return;
|
|
if (botHasThisWeapon(self,item_handgrenades)) return;
|
|
if (botHasThisWeapon(self,item_knife)) return;
|
|
|
|
if (botHasThisWeapon(self,item_blaster)) return;
|
|
|
|
found:
|
|
//FOUND
|
|
// set visible model vwep with 3.20 code
|
|
if (oldweapon != client->pers.weapon)
|
|
ShowGun(self);
|
|
};
|
|
|
|
int botHasWeaponForAmmo (gclient_t *client, gitem_t *item)//WORKS ACRID
|
|
{
|
|
switch (item->tag)
|
|
{
|
|
case (AMMO_SHELLS) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_shotgun)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_supershotgun)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_infecteddart)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_poisondart)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_shc)]);// ||
|
|
//3/99 client->pers.inventory[ITEM_INDEX(item_knife)] );
|
|
}
|
|
|
|
case (AMMO_ROCKETS) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_rocketlauncher)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_pelletrocketlauncher)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_rocketnapalmlauncher)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_stingerrocketlauncher)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_rocketclusterlauncher)]);
|
|
}
|
|
|
|
case (AMMO_CELLS) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_hyperblaster)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_telsacoil)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_flamethrower)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_bfg10k)]);
|
|
}
|
|
|
|
case (AMMO_BULLETS) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_chaingun)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_pulsecannon)]||
|
|
client->pers.inventory[ITEM_INDEX(item_needler)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_ak47)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_pistol)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_machinegun)]);
|
|
}
|
|
|
|
case (AMMO_SLUGS) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_railgun)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_sniperrifle)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_lightninggun)]);
|
|
}
|
|
|
|
case (AMMO_GRENADES) :
|
|
{
|
|
return (client->pers.inventory[ITEM_INDEX(item_grenadelauncher)] ||
|
|
client->pers.inventory[ITEM_INDEX(item_handgrenades)]);
|
|
}
|
|
|
|
default :
|
|
{
|
|
gi.dprintf("botHasWeaponForAmmo: unkown ammo type - %i\n", item->ammo);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
/*//Acrid coed
|
|
int ClientHasAnyWeapon(gclient_t *client)
|
|
{
|
|
if (client->pers.weapon != item_blaster)
|
|
return true;
|
|
|
|
if (client->pers.inventory[ITEM_INDEX(item_shotgun)])
|
|
botDebugPrint("HAS ANY WEP (SHOTGUN)\n");
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_supershotgun)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_machinegun)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_chaingun)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_grenadelauncher)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_rocketlauncher)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_railgun)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_hyperblaster)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_bfg10k)])
|
|
return true;
|
|
//FIXME ACRID NEW WEAPONS
|
|
if (client->pers.inventory[ITEM_INDEX(item_sniperrifle)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_lightninggun)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_pulsecannon)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_telsacoil)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_needler)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_flamethrower)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_pelletrocketlauncher)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_rocketnapalmlauncher)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_rocketclusterlauncher)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_shc)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_handgrenades)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_ak47)])
|
|
return true;
|
|
if (client->pers.inventory[ITEM_INDEX(item_pistol)])
|
|
return true;
|
|
return false;
|
|
}
|
|
*///acrid coed
|
|
int botCanPickupAmmo (gclient_t *client, gitem_t *item)
|
|
{
|
|
int max, index;
|
|
|
|
switch (item->tag)
|
|
{
|
|
case AMMO_BULLETS : max = client->pers.max_bullets; break;
|
|
case AMMO_SHELLS : max = client->pers.max_shells; break;
|
|
case AMMO_ROCKETS : max = client->pers.max_rockets; break;
|
|
case AMMO_GRENADES : max = client->pers.max_grenades; break;
|
|
case AMMO_CELLS : max = client->pers.max_cells; break;
|
|
case AMMO_SLUGS : max = client->pers.max_slugs; break;
|
|
default : return false;
|
|
}
|
|
|
|
//gi.dprintf("Max ammo set\n");
|
|
|
|
index = ITEM_INDEX(item);
|
|
|
|
if (client->pers.inventory[index] == max)
|
|
return false;
|
|
|
|
//gi.dprintf("Can pickup ammo\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
void GetBotFireForWeapon(gitem_t *weapon, void (**bot_fire)(edict_t *self))
|
|
{
|
|
if (weapon == item_blaster)
|
|
*bot_fire = botBlaster;//acrid
|
|
if (weapon == item_rocketlauncher)
|
|
*bot_fire = botRocketLauncher;
|
|
else if (weapon == item_chaingun)
|
|
*bot_fire = botChaingun;
|
|
else if (weapon == item_supershotgun)
|
|
*bot_fire = botSuperShotgun;
|
|
else if (weapon == item_grenadelauncher)
|
|
*bot_fire = botGrenadeLauncher;
|
|
else if (weapon == item_railgun)
|
|
*bot_fire = botRailgun;
|
|
else if (weapon == item_hyperblaster)
|
|
*bot_fire = botHyperblaster;
|
|
else if (weapon == item_bfg10k)
|
|
*bot_fire = botBFG;
|
|
else if (weapon == item_shotgun)
|
|
*bot_fire = botShotgun;
|
|
else if (weapon == item_machinegun)
|
|
*bot_fire = botMachineGun;
|
|
//ACRID
|
|
else if (weapon == item_lightninggun)
|
|
*bot_fire = botLightningGun;
|
|
else if (weapon == item_infecteddart)
|
|
*bot_fire = botInfectedDart;
|
|
else if (weapon == item_pulsecannon)
|
|
*bot_fire = botPulseCannon;
|
|
else if (weapon == item_telsacoil)
|
|
*bot_fire = botTeslaCoil;
|
|
else if (weapon == item_sniperrifle)
|
|
*bot_fire = botTeslaCoil;
|
|
else if (weapon == item_flamethrower)
|
|
*bot_fire = botFlameThrower;
|
|
else if (weapon == item_pelletrocketlauncher)
|
|
*bot_fire = botPelletRocketLauncher;
|
|
else if (weapon == item_rocketnapalmlauncher)
|
|
*bot_fire = botRocketNapalmLauncher;
|
|
else if (weapon == item_rocketclusterlauncher)
|
|
*bot_fire = botRocketClusterLauncher;
|
|
else if (weapon == item_stingerrocketlauncher)
|
|
*bot_fire = botStingerRocketLauncher;
|
|
else if (weapon == item_needler)
|
|
*bot_fire = botNeedler;
|
|
else if (weapon == item_shc)
|
|
*bot_fire = botSHC;
|
|
else if (weapon == item_handgrenades)
|
|
*bot_fire = botGrenades;
|
|
else if (weapon == item_poisondart)
|
|
*bot_fire = botPoisonDart;
|
|
else if (weapon == item_ak47)
|
|
*bot_fire = botAk47;
|
|
else if (weapon == item_pistol)
|
|
*bot_fire = botPistol;
|
|
else if (weapon == item_knife)
|
|
*bot_fire = botKnife;
|
|
//ACRID END
|
|
}
|
|
|
|
/*
|
|
===================
|
|
botPickBestCloseWeapon
|
|
|
|
called when close to enemy, don't use RL, GL, BFG
|
|
===================
|
|
*/
|
|
void botPickBestCloseWeapon(edict_t *self)
|
|
{
|
|
botDebugPrint("best close weapon\n");
|
|
//INFECTED DART CLOSE
|
|
//PELLET ROCKETLAUNCHER CLOSE
|
|
//PISTOL CLOSE
|
|
//AK47 CLOSE
|
|
//PULSECANNON CLOSE
|
|
//LIGHNING GUN CLOSE
|
|
//NEEDLER CLOSE
|
|
//FLAME THROWER CLOSE
|
|
//NAPALM ROCKETLAUNCHER CLOSE
|
|
//SHC CLOSE
|
|
//CHAINGUN CLOSE
|
|
//SUPER SHOTGUN CLOSE
|
|
//HYPERBLASTER CLOSE
|
|
//GRENADE FIXME ACRID CLOSE
|
|
//MACHINEGUN CLOSE
|
|
//POISON DART CLOSE
|
|
//TESLA COIL CLOSE
|
|
//BFG CLOSE
|
|
//RAILGUN CLOSE
|
|
//CLUSTER ROCKETLAUNCHER CLOSE
|
|
//SHOTGUN CLOSE
|
|
//GRENADE LAUNCHER CLOSE
|
|
//ROCKETS CLOSE
|
|
if (botHasThisWeapon(self,item_knife)) return;
|
|
if (botHasThisWeapon(self,item_infecteddart)) return;
|
|
if (botHasThisWeapon(self,item_pelletrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_pistol)) return;
|
|
if (botHasThisWeapon(self,item_ak47)) return;
|
|
if (botHasThisWeapon(self,item_pulsecannon)) return;
|
|
if (botHasThisWeapon(self,item_lightninggun)) return;
|
|
if (botHasThisWeapon(self,item_needler)) return;
|
|
if (botHasThisWeapon(self,item_flamethrower)) return;
|
|
if (botHasThisWeapon(self,item_rocketnapalmlauncher)) return;
|
|
if (botHasThisWeapon(self,item_shc)) return;
|
|
if (botHasThisWeapon(self,item_chaingun)) return;
|
|
if (botHasThisWeapon(self,item_supershotgun)) return;
|
|
if (botHasThisWeapon(self,item_hyperblaster)) return;
|
|
if (botHasThisWeapon(self,item_handgrenades)) return;
|
|
if (botHasThisWeapon(self,item_machinegun)) return;
|
|
if (botHasThisWeapon(self,item_poisondart)) return;
|
|
if (botHasThisWeapon(self,item_telsacoil)) return;
|
|
if (botHasThisWeapon(self,item_bfg10k)) return;
|
|
if (botHasThisWeapon(self,item_railgun)) return;
|
|
if (botHasThisWeapon(self,item_rocketclusterlauncher)) return;
|
|
if (botHasThisWeapon(self,item_shotgun)) return;
|
|
if (botHasThisWeapon(self,item_grenadelauncher))return;
|
|
if (botHasThisWeapon(self,item_stingerrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_rocketlauncher)) return;
|
|
|
|
if (botHasThisWeapon(self,item_blaster)) return;
|
|
|
|
};
|
|
|
|
/*
|
|
===================
|
|
botPickBestFarWeapon
|
|
|
|
called when far from enemy
|
|
===================
|
|
*/
|
|
void botPickBestFarWeapon(edict_t *self)
|
|
{
|
|
|
|
botDebugPrint("best far weapon\n");
|
|
//BFG FAR
|
|
//AK47 FAR
|
|
//NAPALM ROCKETLAUNCHER FAR
|
|
//CLUSTER ROCKETLAUNCHER FAR
|
|
//ROCKETS FAR
|
|
//RAILGUN FAR
|
|
//TESLA COIL FAR
|
|
//LIGHNING GUN FAR
|
|
//INFECTED DART FAR
|
|
//POISON DART FAR
|
|
//HYPERBLASTER FAR
|
|
//PELLET ROCKETLAUNCHER FAR
|
|
//CHAINGUN FAR
|
|
//MACHINE GUN FAR
|
|
//FLAME THROWER FAR
|
|
//GRENADE LAUNCHER FAR
|
|
//SHC FAR
|
|
//SUPER SHOTGUN FAR
|
|
//PISTOL FAR
|
|
//SHOTGUN FAR
|
|
//NEEDLER FAR
|
|
if (botHasThisWeapon(self,item_bfg10k)) return;
|
|
if (botHasThisWeapon(self,item_rocketnapalmlauncher)) return;
|
|
if (botHasThisWeapon(self,item_rocketclusterlauncher)) return;
|
|
if (botHasThisWeapon(self,item_rocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_railgun)) return;
|
|
if (botHasThisWeapon(self,item_ak47)) return;
|
|
if (botHasThisWeapon(self,item_stingerrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_pulsecannon)) return;
|
|
if (botHasThisWeapon(self,item_lightninggun)) return;
|
|
if (botHasThisWeapon(self,item_chaingun)) return;
|
|
if (botHasThisWeapon(self,item_hyperblaster)) return;
|
|
if (botHasThisWeapon(self,item_pelletrocketlauncher)) return;
|
|
if (botHasThisWeapon(self,item_telsacoil)) return;
|
|
if (botHasThisWeapon(self,item_infecteddart)) return;
|
|
if (botHasThisWeapon(self,item_poisondart)) return;
|
|
if (botHasThisWeapon(self,item_grenadelauncher))return;
|
|
if (botHasThisWeapon(self,item_flamethrower)) return;
|
|
if (botHasThisWeapon(self,item_machinegun)) return;
|
|
if (botHasThisWeapon(self,item_shc)) return;
|
|
if (botHasThisWeapon(self,item_supershotgun)) return;
|
|
if (botHasThisWeapon(self,item_pistol)) return;
|
|
if (botHasThisWeapon(self,item_shotgun)) return;
|
|
if (botHasThisWeapon(self,item_needler)) return;
|
|
if (botHasThisWeapon(self,item_handgrenades)) return;
|
|
if (botHasThisWeapon(self,item_knife)) return;
|
|
|
|
if (botHasThisWeapon(self,item_blaster)) return;
|
|
};
|