3625 lines
84 KiB
C
3625 lines
84 KiB
C
/* p_client.c */
|
|
|
|
#include "g_local.h"
|
|
#include "m_player.h"
|
|
#include "stdlog.h" // StdLog - Mark Davies
|
|
#include "wf_classmgr.h"//acrid for WFCopyToBodyQue
|
|
|
|
FILE *zbotfile = NULL;
|
|
|
|
int deas = 10;
|
|
|
|
//ERASER START
|
|
///Q2 Camera Begin
|
|
#include "camclient.h"
|
|
///Q2 Camera End
|
|
#include "p_trail.h"
|
|
#include "bot_procs.h"
|
|
#include <sys/timeb.h>
|
|
#include <time.h>
|
|
#define OPTIMIZE_INTERVAL 0.1
|
|
int num_clients = 0;
|
|
//ERASER END
|
|
|
|
void ClientUserinfoChanged (edict_t *ent, char *userinfo);
|
|
void SP_misc_teleporter_dest (edict_t *ent);
|
|
void WFRemoveDisguise(edict_t *ent);
|
|
void Drop_General (edict_t *ent, gitem_t *item);
|
|
void HealPlayer(edict_t *ent);
|
|
void I_AM_A_ZBOT(edict_t *ent);
|
|
|
|
qboolean ZbotCheck(edict_t *ent, usercmd_t *ucmd);
|
|
|
|
//Utility function to fix player name so it doesnt have a '%' in it
|
|
void wfFixName(char *text)
|
|
{
|
|
int j;
|
|
j = 0;
|
|
while (j < 150 && text[j] != 0)
|
|
{
|
|
if (text[j] == '%') text[j] = '_'; // don't allow formated characters in text
|
|
++j;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Gross, ugly, disgustuing hack section
|
|
//
|
|
|
|
// this function is an ugly as hell hack to fix some map flaws
|
|
//
|
|
// the coop spawn spots on some maps are SNAFU. There are coop spots
|
|
// with the wrong targetname as well as spots with no name at all
|
|
//
|
|
// we use carnal knowledge of the maps to fix the coop spot targetnames to match
|
|
// that of the nearest named single player spot
|
|
|
|
|
|
static void SP_FixCoopSpots (edict_t *self)
|
|
{
|
|
edict_t *spot;
|
|
vec3_t d;
|
|
|
|
spot = NULL;
|
|
|
|
while(1)
|
|
{
|
|
spot = G_Find(spot, FOFS(classname), "info_player_start");
|
|
if (!spot)
|
|
return;
|
|
if (!spot->targetname)
|
|
continue;
|
|
VectorSubtract(self->s.origin, spot->s.origin, d);
|
|
if (VectorLength(d) < 384)
|
|
{
|
|
if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0)
|
|
{
|
|
// gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname);
|
|
self->targetname = spot->targetname;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now if that one wasn't ugly enough for you then try this one on for size
|
|
// some maps don't have any coop spots at all, so we need to create them
|
|
// where they should have been
|
|
|
|
static void SP_CreateCoopSpots (edict_t *self)
|
|
{
|
|
edict_t *spot;
|
|
|
|
if(Q_stricmp(level.mapname, "security") == 0)
|
|
{
|
|
spot = G_Spawn();
|
|
spot->classname = "info_player_coop";
|
|
spot->s.origin[0] = 188 - 64;
|
|
spot->s.origin[1] = -164;
|
|
spot->s.origin[2] = 80;
|
|
spot->targetname = "jail3";
|
|
spot->s.angles[1] = 90;
|
|
|
|
spot = G_Spawn();
|
|
spot->classname = "info_player_coop";
|
|
spot->s.origin[0] = 188 + 64;
|
|
spot->s.origin[1] = -164;
|
|
spot->s.origin[2] = 80;
|
|
spot->targetname = "jail3";
|
|
spot->s.angles[1] = 90;
|
|
|
|
spot = G_Spawn();
|
|
spot->classname = "info_player_coop";
|
|
spot->s.origin[0] = 188 + 128;
|
|
spot->s.origin[1] = -164;
|
|
spot->s.origin[2] = 80;
|
|
spot->targetname = "jail3";
|
|
spot->s.angles[1] = 90;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
|
|
The normal starting point for a level.
|
|
*/
|
|
void SP_info_player_start(edict_t *self)
|
|
{
|
|
if (!coop->value)
|
|
return;
|
|
if(Q_stricmp(level.mapname, "security") == 0)
|
|
{
|
|
// invoke one of our gross, ugly, disgusting hacks
|
|
self->think = SP_CreateCoopSpots;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
}
|
|
}
|
|
|
|
//ERASER START
|
|
edict_t *dm_spots[64];
|
|
int num_dm_spots;
|
|
//ERASER END
|
|
|
|
/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
|
|
potential spawning position for deathmatch games
|
|
*/
|
|
void SP_info_player_deathmatch(edict_t *self)
|
|
{
|
|
if (!deathmatch->value)
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
SP_misc_teleporter_dest (self);
|
|
|
|
dm_spots[num_dm_spots++] = self;//ERASER
|
|
}
|
|
|
|
/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
|
|
potential spawning position for coop games
|
|
*/
|
|
|
|
void SP_info_player_coop(edict_t *self)
|
|
{
|
|
if (!coop->value)
|
|
{
|
|
G_FreeEdict (self);
|
|
return;
|
|
}
|
|
|
|
if((Q_stricmp(level.mapname, "jail2") == 0) ||
|
|
(Q_stricmp(level.mapname, "jail4") == 0) ||
|
|
(Q_stricmp(level.mapname, "mine1") == 0) ||
|
|
(Q_stricmp(level.mapname, "mine2") == 0) ||
|
|
(Q_stricmp(level.mapname, "mine3") == 0) ||
|
|
(Q_stricmp(level.mapname, "mine4") == 0) ||
|
|
(Q_stricmp(level.mapname, "lab") == 0) ||
|
|
(Q_stricmp(level.mapname, "boss1") == 0) ||
|
|
(Q_stricmp(level.mapname, "fact3") == 0) ||
|
|
(Q_stricmp(level.mapname, "biggun") == 0) ||
|
|
(Q_stricmp(level.mapname, "space") == 0) ||
|
|
(Q_stricmp(level.mapname, "command") == 0) ||
|
|
(Q_stricmp(level.mapname, "power2") == 0) ||
|
|
(Q_stricmp(level.mapname, "strike") == 0))
|
|
{
|
|
// invoke one of our gross, ugly, disgusting hacks
|
|
self->think = SP_FixCoopSpots;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
}
|
|
}
|
|
|
|
|
|
/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
|
|
The deathmatch intermission point will be at one of these
|
|
Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll'
|
|
*/
|
|
void SP_info_player_intermission(void)
|
|
{
|
|
}
|
|
|
|
|
|
/*QUAKED info_position (1 0 1) (-16 -16 -24) (16 16 32)
|
|
Used to describe the location within a map
|
|
Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll'
|
|
*/
|
|
void SP_info_position(edict_t *ent)
|
|
{
|
|
/*
|
|
gi.dprintf("DEBUG (Spawned info_position) Classname = %s\n", ent->classname);
|
|
if (ent->message && ent->message[0])
|
|
gi.dprintf("DEBUG (info_position) Message = %s\n", ent->message);
|
|
*/
|
|
|
|
}
|
|
|
|
/*QUAKED item_flagreturn (1 0 1) (-16 -16 -24) (16 16 32)
|
|
Used to describe the location of the flag return base
|
|
*/
|
|
void SP_item_flagreturn(edict_t *ent)
|
|
{
|
|
|
|
// gi.dprintf("--Spawned %s. Target = %s, ent = %d\n", ent->classname, ent->target, ent);
|
|
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
|
|
void player_pain (edict_t *self, edict_t *other, float kick, int damage)
|
|
{
|
|
// player pain is handled at the end of the frame in P_DamageFeedback
|
|
}
|
|
|
|
|
|
qboolean IsFemale (edict_t *ent)
|
|
{
|
|
|
|
if (!ent->client)
|
|
return false;
|
|
|
|
if (ent->client->player_model == CLASS_MODEL_FEMALE)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//WF
|
|
qboolean IsCyborg (edict_t *ent)
|
|
{
|
|
|
|
|
|
if (!ent->client)
|
|
return false;
|
|
|
|
if (ent->client->player_model == CLASS_MODEL_CYBORG)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
//WF
|
|
|
|
//Not female, male or cyborg?
|
|
qboolean IsNeutral (edict_t *ent)
|
|
{
|
|
|
|
if (!ent->client)
|
|
return false;
|
|
|
|
if ((ent->client->player_model == CLASS_MODEL_FEMALE) ||
|
|
(ent->client->player_model == CLASS_MODEL_MALE) ||
|
|
(ent->client->player_model == CLASS_MODEL_CYBORG))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
int PlayerChangeScore(edict_t *self, int points)
|
|
{
|
|
if (wf_game.game_halted)
|
|
{
|
|
safe_cprintf(self, PRINT_HIGH, "No Scoring Allowed While Game Is Suspended\n");
|
|
return false;
|
|
}
|
|
else
|
|
self->client->resp.score += points;
|
|
return true;
|
|
}
|
|
|
|
|
|
void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *atk)
|
|
{
|
|
int mod;
|
|
char *message;
|
|
char *message2;
|
|
qboolean ff;
|
|
edict_t *attacker;
|
|
|
|
qboolean special_message;
|
|
|
|
special_message = false;
|
|
|
|
attacker = atk;
|
|
|
|
//If this is a death by sentry gun, then the attacker's owner
|
|
//is the real attacker
|
|
if ((atk->creator) &&(!atk->client) && (meansOfDeath == MOD_SENTRY || meansOfDeath == MOD_SENTRY_ROCKET || meansOfDeath == MOD_SHRAPNEL || meansOfDeath == MOD_PELLET) )
|
|
attacker = atk->creator;
|
|
|
|
if (coop->value && attacker->client)
|
|
meansOfDeath |= MOD_FRIENDLY_FIRE;
|
|
|
|
//ERASER START //ACRID FIXME? teamplay
|
|
else if (deathmatch->value && teamplay->value && attacker->client && SameTeam(self, attacker))
|
|
{
|
|
meansOfDeath |= MOD_FRIENDLY_FIRE;
|
|
}
|
|
//ERASER END
|
|
|
|
if (deathmatch->value || coop->value)
|
|
{
|
|
ff = meansOfDeath & MOD_FRIENDLY_FIRE;
|
|
mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
|
|
message = NULL;
|
|
message2 = "";
|
|
|
|
switch (mod)
|
|
{
|
|
case MOD_SUICIDE:
|
|
message = "suicides";
|
|
break;
|
|
case MOD_FALLING:
|
|
message = "cratered";
|
|
break;
|
|
case MOD_CRUSH:
|
|
message = "was squished";
|
|
break;
|
|
case MOD_WATER:
|
|
message = "sank like a rock";
|
|
break;
|
|
case MOD_SLIME:
|
|
message = "melted";
|
|
break;
|
|
case MOD_LAVA:
|
|
message = "does a back flip into the lava";
|
|
break;
|
|
case MOD_FEIGN:
|
|
message = "died while trying to feign";
|
|
break;
|
|
case MOD_EXPLOSIVE:
|
|
case MOD_BARREL:
|
|
message = "blew up";
|
|
break;
|
|
case MOD_EXIT:
|
|
message = "found a way out";
|
|
break;
|
|
//TeT - Laser is not suicide
|
|
// case MOD_TARGET_LASER:
|
|
// message = "saw the light";
|
|
// break;
|
|
case MOD_TARGET_BLASTER:
|
|
message = "got blasted";
|
|
break;
|
|
case MOD_BOMB:
|
|
case MOD_SPLASH:
|
|
case MOD_TRIGGER_HURT:
|
|
message = "was in the wrong place";
|
|
break;
|
|
}
|
|
|
|
if (attacker == self)
|
|
{
|
|
switch (mod)
|
|
{
|
|
case MOD_HELD_GRENADE:
|
|
message = "tried to put the pin back in";
|
|
break;
|
|
case MOD_HG_SPLASH:
|
|
case MOD_G_SPLASH:
|
|
if (IsNeutral(self))
|
|
message = "tripped on its own grenade";
|
|
else if (IsFemale(self))
|
|
message = "tripped on her own grenade";
|
|
else
|
|
message = "tripped on his own grenade";
|
|
break;
|
|
case MOD_R_SPLASH:
|
|
if (IsNeutral(self))
|
|
message = "blew itself up";
|
|
else if (IsFemale(self))
|
|
message = "blew herself up";
|
|
else
|
|
message = "blew himself up";
|
|
break;
|
|
case MOD_BFG_BLAST:
|
|
message = "should have used a smaller gun";
|
|
break;
|
|
case MOD_FEIGN:
|
|
message = "died while trying to feign";
|
|
break;
|
|
default:
|
|
if (IsNeutral(self))
|
|
message = "killed itself";
|
|
else if (IsFemale(self))
|
|
message = "killed herself";
|
|
else
|
|
message = "killed himself";
|
|
break;
|
|
}
|
|
}
|
|
if (message)
|
|
{
|
|
my_bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message);
|
|
if (deathmatch->value)
|
|
{
|
|
if (PlayerChangeScore(self,CTF_SUICIDE_POINTS))
|
|
sl_LogScore( &gi, self->client->pers.netname, "", "Suicide",mod, CTF_SUICIDE_POINTS ); // StdLog
|
|
}
|
|
self->enemy = NULL;
|
|
|
|
if (special_message)
|
|
{
|
|
// safe_cprintf(self, PRINT_HIGH, "[YOU KILLED YOURSELF: ");
|
|
// if ((inflictor) && (inflictor->classname))
|
|
// safe_cprintf(self, PRINT_HIGH, " item=%s",inflictor->classname);
|
|
// safe_cprintf(self, PRINT_HIGH, "]\n");
|
|
}
|
|
|
|
|
|
//if (wfdebug) gi.dprintf("diseased = %d. Diseased by = %d\n", self->disease, self->diseased_by);
|
|
|
|
//If player is diseased, give credit to nurse who infected them
|
|
if(self->disease && self->diseased_by && self->diseased_by->client && self->diseased_by->inuse)
|
|
{
|
|
if (PlayerChangeScore(self->diseased_by,CTF_FRAG_POINTS))
|
|
sl_LogScore( &gi, self->diseased_by->client->pers.netname, self->client->pers.netname, "Kill",mod, CTF_FRAG_POINTS ); // StdLog - Mark Davies
|
|
|
|
my_bprintf (PRINT_MEDIUM,"%s gets a frag for %s's suicide while diseased.\n", self->diseased_by->client->pers.netname, self->client->pers.netname);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
self->enemy = attacker;
|
|
if (attacker && attacker->client)
|
|
{
|
|
switch (mod)
|
|
{
|
|
case MOD_BLASTER:
|
|
message = "was blasted by";
|
|
break;
|
|
case MOD_SHOTGUN:
|
|
message = "was gunned down by";
|
|
break;
|
|
case MOD_SSHOTGUN:
|
|
message = "was blown away by";
|
|
message2 = "'s super shotgun";
|
|
break;
|
|
case MOD_MACHINEGUN:
|
|
message = "was machinegunned by";
|
|
break;
|
|
case MOD_CHAINGUN:
|
|
message = "was cut in half by";
|
|
message2 = "'s chaingun";
|
|
break;
|
|
case MOD_GRENADE:
|
|
message = "was popped by";
|
|
message2 = "'s grenade";
|
|
break;
|
|
case MOD_G_SPLASH:
|
|
message = "was shredded by";
|
|
message2 = "'s shrapnel";
|
|
break;
|
|
case MOD_ROCKET:
|
|
message = "ate";
|
|
message2 = "'s rocket";
|
|
break;
|
|
case MOD_MISSILE:
|
|
message = "was eliminated by";
|
|
message2 = "'s missile launcher";
|
|
break;
|
|
case MOD_HOMINGROCKET:
|
|
message = "was slammed";
|
|
message2 = "'s homing rocket";
|
|
break;
|
|
case MOD_R_SPLASH:
|
|
message = "almost dodged";
|
|
message2 = "'s rocket";
|
|
break;
|
|
case MOD_HYPERBLASTER:
|
|
message = "was melted by";
|
|
message2 = "'s hyperblaster";
|
|
break;
|
|
case MOD_RAILGUN:
|
|
message = "was railed by";
|
|
break;
|
|
case MOD_BFG_LASER:
|
|
message = "saw the pretty lights from";
|
|
message2 = "'s BFG";
|
|
break;
|
|
case MOD_BFG_BLAST:
|
|
message = "was disintegrated by";
|
|
message2 = "'s BFG blast";
|
|
break;
|
|
case MOD_BFG_EFFECT:
|
|
message = "couldn't hide from";
|
|
message2 = "'s BFG";
|
|
break;
|
|
case MOD_HANDGRENADE:
|
|
message = "caught";
|
|
message2 = "'s handgrenade";
|
|
break;
|
|
case MOD_HG_SPLASH:
|
|
message = "didn't see";
|
|
message2 = "'s handgrenade";
|
|
break;
|
|
case MOD_HELD_GRENADE:
|
|
message = "feels";
|
|
message2 = "'s pain";
|
|
break;
|
|
case MOD_TELEFRAG:
|
|
message = "tried to invade";
|
|
message2 = "'s personal space";
|
|
break;
|
|
//ZOID
|
|
case MOD_GRAPPLE:
|
|
message = "was caught by";
|
|
message2 = "'s grapple";
|
|
break;
|
|
//ZOID
|
|
|
|
//WF
|
|
case MOD_WF_LASERBALL:
|
|
message = "was fried by";
|
|
message2 = "'s laserball";
|
|
break;
|
|
|
|
case MOD_WF_GOODYEAR:
|
|
message = "was popped by";
|
|
message2 = "'s goodyear grenade";
|
|
break;
|
|
|
|
case MOD_WF_PROXIMITY:
|
|
message = "swallowed";
|
|
message2 = "'s proximity grenade";
|
|
break;
|
|
|
|
case MOD_WF_CLUSTER:
|
|
message = "was ripped by";
|
|
message2 = "'s cluster grenade";
|
|
break;
|
|
|
|
case MOD_WF_PIPEBOMB:
|
|
message = "fell for";
|
|
message2 = "'s pipebomb";
|
|
break;
|
|
|
|
case MOD_WF_EARTHQUAKE:
|
|
message = "was shaken by";
|
|
message2 = "'s earthquake";
|
|
break;
|
|
|
|
case MOD_WF_FLAME:
|
|
message = "was burned by";
|
|
message2 = "'s flame";
|
|
break;
|
|
|
|
case MOD_FLAMETHROWER:
|
|
message = "was charred by";
|
|
message2 = "'s flame-thrower";
|
|
break;
|
|
|
|
case MOD_REVERSE_TELEFRAG:
|
|
message = "was reverse telefragged by";
|
|
break;
|
|
case MOD_NAG:
|
|
message = "was NAGed to death by";
|
|
break;
|
|
case MOD_SHRAPNEL:
|
|
message = "was pithed by";
|
|
message2 = "'s shrapnel grenade";
|
|
break;
|
|
case MOD_CLUSTERROCKET:
|
|
message = "was splattered by";
|
|
message2 = "'s cluster rocket";
|
|
break;
|
|
case MOD_DISEASE:
|
|
message = "died from";
|
|
message2 = "'s disease";
|
|
break;
|
|
|
|
case MOD_SNIPERRIFLE:
|
|
message = "was slaughtered by";
|
|
message2 = "'s sniper rifle";
|
|
break;
|
|
case MOD_SNIPERRIFLE_LEG:
|
|
message = "had their legs shot out by";
|
|
message2 = "'s sniper rifle";
|
|
break;
|
|
case MOD_SNIPERRIFLE_HEAD:
|
|
message = "had his head blown off by";
|
|
message2 = "'s sniper rifle";
|
|
break;
|
|
|
|
case MOD_SHC:
|
|
message = "burst into flames from";
|
|
message2 = "'s SHC rifle";
|
|
break;
|
|
|
|
case MOD_NEEDLER:
|
|
message = "was needled by";
|
|
break;
|
|
|
|
case MOD_CONCUSSION:
|
|
message = "lost his head from";
|
|
message2 = "'s concussion grenade";
|
|
break;
|
|
|
|
case MOD_ARMORDART:
|
|
message = "was pierced by";
|
|
message2 = "'s dart";
|
|
break;
|
|
|
|
case MOD_INFECTEDDART:
|
|
message = "was infected by";
|
|
message2 = "'s dart";
|
|
break;
|
|
|
|
case MOD_NAPALMROCKET:
|
|
message = "was smoked by";
|
|
message2 = "'s napalm rocket";
|
|
break;
|
|
case MOD_NAPALMGRENADE:
|
|
message = "got crispy from";
|
|
message2 = "'s napalm grenade";
|
|
break;
|
|
|
|
case MOD_LIGHTNING:
|
|
message = "saw lightning from";
|
|
message2 = "'s lightning gun";
|
|
break;
|
|
|
|
case MOD_TELSA:
|
|
message = "was exposed to";
|
|
message2 = "'s telsa coil";
|
|
break;
|
|
|
|
case MOD_MAGNOTRON:
|
|
message = "was sucked into";
|
|
message2 = "'s magnotron";
|
|
break;
|
|
|
|
case MOD_SHOCK:
|
|
message = "was shocked by";
|
|
break;
|
|
|
|
case MOD_PELLET:
|
|
message = "was pelted by";
|
|
break;
|
|
|
|
case MOD_FLAREGUN:
|
|
message = "was embarrassed to be toasted by";
|
|
message2 = "'s flare gun";
|
|
break;
|
|
|
|
case MOD_FLARE:
|
|
message = "was snuffed by";
|
|
message2 = "'s flare";
|
|
break;
|
|
|
|
case MOD_TRANQUILIZER:
|
|
message = "slowed to a stop from";
|
|
message2 = "'s tranquilizer";
|
|
break;
|
|
|
|
case MOD_LRPROJECTILE:
|
|
message = "was blasted by";
|
|
message2 = "'s LR Projectile Launcher";
|
|
break;
|
|
|
|
case MOD_BOLTEDBLASTER:
|
|
message = "expired from";
|
|
message2 = "'s bolted blaster";
|
|
break;
|
|
|
|
case MOD_WF_TURRET:
|
|
message="caught Turret's Syndrome from";
|
|
message2="'s Turret Grenade.";
|
|
break;
|
|
|
|
case MOD_PLASMABOMB:
|
|
message = "was vaporized by";
|
|
break;
|
|
case MOD_NAIL:
|
|
message = "was nailed by";
|
|
//message2 = "";
|
|
break;
|
|
case MOD_MBPC:
|
|
message = "was disintegrated by";
|
|
message2 = "'s Pulse Cannon";
|
|
break;
|
|
case MOD_SENTRY:
|
|
message = "was laid low by";
|
|
message2 = "'s Sentry Gun";
|
|
break;
|
|
case MOD_SENTRY_ROCKET:
|
|
message = "was liquidated by";
|
|
message2 = "'s Sentry Gun Rocket";
|
|
break;
|
|
case MOD_KAMIKAZE:
|
|
message = "didn't survive";
|
|
message2 = "'s Kamikaze run";
|
|
break;
|
|
case MOD_TRANQUILIZERDART:
|
|
message = "did not escape";
|
|
message2 = "'s tranquilizer dart";
|
|
break;
|
|
case MOD_DEPOT:
|
|
message = "was blown to bits by";
|
|
message2 = "'s supply depot";
|
|
break;
|
|
case MOD_DEPOT_EXPLODE:
|
|
message = "was in the way when";
|
|
message2 = " blew up a supply depot";
|
|
break;
|
|
case MOD_HEALINGDEPOT:
|
|
message = "was blown to bits by";
|
|
message2 = "'s healing depot";
|
|
break;
|
|
case MOD_MEGACHAINGUN:
|
|
message = "was cut to ribbons";
|
|
message2 = "'s mega chaingun";
|
|
break;
|
|
case MOD_SENTRYKILLER:
|
|
message = "got in the way of ";
|
|
message2 = "'s Sentry Killer Rocket";
|
|
break;
|
|
|
|
case MOD_KNIFE:
|
|
message = "was punctured by";
|
|
message2 = "'s knife";
|
|
break;
|
|
|
|
case MOD_STINGER:
|
|
message = "was stopped by";
|
|
message2 = "'s stinger";
|
|
break;
|
|
|
|
case MOD_KNIFEBACK:
|
|
message = "was stabbed in the back by";
|
|
message2 = "'s knife";
|
|
break;
|
|
|
|
case MOD_FREEZER:
|
|
message = "was iced by";//acrid 3/99 death msg
|
|
message2 = "'s freezer";
|
|
break;
|
|
|
|
case MOD_LASERCUTTER:
|
|
message = "was sliced and diced by";
|
|
message2 = "'s laser cutter grenade";
|
|
break;
|
|
|
|
case MOD_TESLA:
|
|
message = "was electrified by";
|
|
message2 = "'s tesla grenade";
|
|
break;
|
|
|
|
case MOD_GASGRENADE:
|
|
message = "was fumigated by";
|
|
message2 = "'s gas grenade";
|
|
break;
|
|
|
|
case MOD_AK47:
|
|
message = "became swiss cheese from";
|
|
message2 = "'s AK47";
|
|
break;
|
|
|
|
case MOD_PISTOL:
|
|
message = "was assassinated by ";
|
|
message2 = "'s pistol";
|
|
break;
|
|
|
|
case MOD_TARGET_LASER:
|
|
message = "saw";
|
|
message2 = "'s light";
|
|
break;
|
|
|
|
case MOD_CAMERA:
|
|
message = "smiled for";
|
|
message2 = "'s camera";
|
|
break;
|
|
|
|
|
|
}
|
|
if (message)
|
|
{
|
|
my_bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
|
|
if (deathmatch->value)
|
|
{
|
|
if (ff)
|
|
{
|
|
if (PlayerChangeScore(attacker,CTF_SUICIDE_POINTS))
|
|
sl_LogScore( &gi, attacker->client->pers.netname, self->client->pers.netname, "Friendly Fire",mod, CTF_SUICIDE_POINTS ); // StdLog - Mark Davies
|
|
}
|
|
else
|
|
{
|
|
if (PlayerChangeScore(attacker,CTF_FRAG_POINTS))
|
|
sl_LogScore( &gi, attacker->client->pers.netname, self->client->pers.netname, "Kill",mod, CTF_FRAG_POINTS ); // StdLog - Mark Davies
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
my_bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname);
|
|
|
|
// safe_cprintf(self, PRINT_HIGH, "[YOU WERE KILLED WITH: ");
|
|
// if ((inflictor) && (inflictor->classname))
|
|
// safe_cprintf(self, PRINT_HIGH, " %s",inflictor->classname);
|
|
// if ((attacker) && (attacker->client) && (attacker->client->pers.netname))
|
|
// safe_cprintf(self, PRINT_HIGH, " BY %s",attacker->client->pers.netname);
|
|
// safe_cprintf(self, PRINT_HIGH, "]\n");
|
|
|
|
if (deathmatch->value)
|
|
PlayerChangeScore(self, -1);
|
|
}
|
|
|
|
|
|
void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
|
|
|
void TossClientWeapon (edict_t *self)
|
|
{
|
|
gitem_t *item;
|
|
edict_t *drop;
|
|
qboolean quad;
|
|
float spread;
|
|
|
|
if (!deathmatch->value)
|
|
return;
|
|
|
|
//WF If player classes are on, don't drop a weapon
|
|
if (self->client && (((int)wfflags->value & WF_NO_PLAYER_CLASSES) == 0))
|
|
return;
|
|
//WF
|
|
|
|
item = self->client->pers.weapon;
|
|
if (! self->client->pers.inventory[self->client->ammo_index] )
|
|
item = NULL;
|
|
if (item && (strcmp (item->pickup_name, "Blaster") == 0))
|
|
item = NULL;
|
|
|
|
if (!((int)(dmflags->value) & DF_QUAD_DROP))
|
|
quad = false;
|
|
else
|
|
quad = (self->client->quad_framenum > (level.framenum + 10));
|
|
|
|
if (item && quad)
|
|
spread = 22.5;
|
|
else
|
|
spread = 0.0;
|
|
|
|
if (item)
|
|
{
|
|
self->client->v_angle[YAW] -= spread;
|
|
drop = Drop_Item (self, item);
|
|
self->client->v_angle[YAW] += spread;
|
|
drop->spawnflags = DROPPED_PLAYER_ITEM;
|
|
}
|
|
|
|
if (quad)
|
|
{
|
|
self->client->v_angle[YAW] += spread;
|
|
drop = Drop_Item (self, FindItemByClassname ("item_quad"));
|
|
self->client->v_angle[YAW] -= spread;
|
|
drop->spawnflags |= DROPPED_PLAYER_ITEM;
|
|
|
|
drop->touch = Touch_Item;
|
|
drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
|
|
drop->think = G_FreeEdict;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
LookAtKiller
|
|
==================
|
|
*/
|
|
void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
|
|
{
|
|
vec3_t dir;
|
|
|
|
if (attacker && attacker != world && attacker != self)
|
|
{
|
|
VectorSubtract (attacker->s.origin, self->s.origin, dir);
|
|
}
|
|
else if (inflictor && inflictor != world && inflictor != self)
|
|
{
|
|
VectorSubtract (inflictor->s.origin, self->s.origin, dir);
|
|
}
|
|
else
|
|
{
|
|
self->client->killer_yaw = self->s.angles[YAW];
|
|
return;
|
|
}
|
|
|
|
if (dir[0])
|
|
self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
|
|
else {
|
|
self->client->killer_yaw = 0;
|
|
if (dir[1] > 0)
|
|
self->client->killer_yaw = 90;
|
|
else if (dir[1] < 0)
|
|
self->client->killer_yaw = -90;
|
|
}
|
|
if (self->client->killer_yaw < 0)
|
|
self->client->killer_yaw += 360;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
player_die
|
|
==================
|
|
*/
|
|
void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
int n;
|
|
|
|
//Reset fov if autozoom was on
|
|
if (self->client->pers.autozoom)
|
|
{
|
|
self->client->ps.fov = 90;
|
|
self->PlayerSnipingZoom = 0;
|
|
}
|
|
|
|
//Free laser sight
|
|
if ( self->lasersight )
|
|
{
|
|
G_FreeEdict(self->lasersight);
|
|
self->lasersight = NULL;
|
|
}
|
|
|
|
VectorClear (self->avelocity);
|
|
|
|
self->takedamage = DAMAGE_YES;
|
|
self->movetype = MOVETYPE_TOSS;
|
|
|
|
self->s.modelindex2 = 0; // remove linked weapon model
|
|
//ZOID
|
|
self->s.modelindex3 = 0; // remove linked ctf flag
|
|
//ZOID
|
|
|
|
self->s.angles[0] = 0;
|
|
self->s.angles[2] = 0;
|
|
|
|
self->s.sound = 0;
|
|
self->client->weapon_sound = 0;
|
|
|
|
self->maxs[2] = -8;
|
|
|
|
// self->solid = SOLID_NOT;
|
|
self->svflags |= SVF_DEADMONSTER;
|
|
|
|
if (!self->deadflag)
|
|
{
|
|
self->client->respawn_time = level.time + 1.0;
|
|
LookAtKiller (self, inflictor, attacker);
|
|
self->client->ps.pmove.pm_type = PM_DEAD;
|
|
ClientObituary (self, inflictor, attacker);
|
|
|
|
//WF
|
|
/*
|
|
//Drop all the keys you are holding
|
|
for (n = 0; n < game.num_items; n++)
|
|
{
|
|
//If this is a key and I'm holding it, then drop it
|
|
if ((deathmatch->value) && (itemlist[n].flags & IT_KEY) )
|
|
{
|
|
if (self->client->pers.inventory[n])
|
|
{
|
|
Drop_General(self, &itemlist[n]);
|
|
//gi.dprintf("Dropping key: %s\n", itemlist[n].classname);
|
|
}
|
|
else
|
|
{
|
|
//gi.dprintf("Key not in inventory: %d: %s\n", n, itemlist[n].classname);
|
|
}
|
|
|
|
}
|
|
}
|
|
*/
|
|
//WF
|
|
WFPlayer_Die (self);
|
|
Kamikaze_Cancel(self);
|
|
|
|
//ZOID
|
|
CTFFragBonuses(self, inflictor, attacker);
|
|
//ZOID
|
|
TossClientWeapon (self);
|
|
//ZOID
|
|
if (self->bot_client)//newgrap 4/99
|
|
CTFPlayerResetGrapple(self);
|
|
else//newgrap 4/99
|
|
// CTFPlayerResetGrapple(self);
|
|
CTFPlayerResetGrapple2(self);//newgrap 4/99
|
|
|
|
CTFDeadDropFlag(self);
|
|
CTFDeadDropTech(self);
|
|
//ZOID
|
|
if (deathmatch->value && !self->client->showscores)
|
|
Cmd_Help_f (self); // show scores
|
|
|
|
// clear inventory
|
|
// this is kind of ugly, but it's how we want to handle keys in coop
|
|
for (n = 0; n < game.num_items; n++)
|
|
{
|
|
if (coop->value && itemlist[n].flags & IT_KEY)
|
|
self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n];
|
|
self->client->pers.inventory[n] = 0;
|
|
}
|
|
|
|
}
|
|
|
|
// remove powerups
|
|
self->client->quad_framenum = 0;
|
|
self->client->invincible_framenum = 0;
|
|
self->client->breather_framenum = 0;
|
|
self->client->enviro_framenum = 0;
|
|
|
|
// clear inventory
|
|
//3.20 memset(self->client->pers.inventory, 0, sizeof(self->client->pers.inventory));
|
|
|
|
//WF & ATTILA begin
|
|
if ( Jet_Active(self) )
|
|
{
|
|
Jet_BecomeExplosion( self, damage );
|
|
/*stop jetting when dead*/
|
|
self->client->Jet_framenum = 0;
|
|
}
|
|
//WF & ATTILA
|
|
|
|
if (self->health < -40)//dont use its fixed || (self->client->player_special & SPECIAL_DISGUISE))
|
|
{ // gib
|
|
gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
|
for (n= 0; n < 4; n++)
|
|
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
|
ThrowClientHead (self, damage);
|
|
//ZOID
|
|
self->client->anim_priority = ANIM_DEATH;
|
|
self->client->anim_end = 0;
|
|
//ZOID
|
|
self->takedamage = DAMAGE_NO;
|
|
}
|
|
else if (self->frozen && !self->deadflag)//acrid 3/99 frozen death animation
|
|
{
|
|
self->s.effects |= EF_COLOR_SHELL;
|
|
self->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
|
|
self->client->anim_priority = ANIM_DEATH;
|
|
self->s.frame = FRAME_stand01;
|
|
self->client->anim_end = FRAME_stand01;
|
|
self->frozenbody = 1;
|
|
gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
else
|
|
{ // normal death
|
|
if (!self->deadflag)
|
|
{
|
|
static int i;
|
|
|
|
i = (i+1)%3;
|
|
// start a death animation
|
|
self->client->anim_priority = ANIM_DEATH;
|
|
if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
self->s.frame = FRAME_crdeath1-1;
|
|
self->client->anim_end = FRAME_crdeath5;
|
|
}
|
|
|
|
else switch (i)
|
|
{
|
|
case 0:
|
|
self->s.frame = FRAME_death101-1;
|
|
self->client->anim_end = FRAME_death106;
|
|
break;
|
|
case 1:
|
|
self->s.frame = FRAME_death201-1;
|
|
self->client->anim_end = FRAME_death206;
|
|
break;
|
|
case 2:
|
|
self->s.frame = FRAME_death301-1;
|
|
self->client->anim_end = FRAME_death308;
|
|
break;
|
|
}
|
|
gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
|
|
}
|
|
}
|
|
|
|
self->deadflag = DEAD_DEAD;
|
|
self->frozen = 0;//acrid 3/99
|
|
|
|
|
|
gi.linkentity (self);
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
/*
|
|
==============
|
|
InitClientPersistant
|
|
|
|
This is only called when the game first initializes in single player,
|
|
but is called after each death and level change in deathmatch
|
|
==============
|
|
*/
|
|
void InitClientPersistant (gclient_t *client)
|
|
{
|
|
gitem_t *item;
|
|
int i;
|
|
int HasVoted = client->pers.HasVoted;
|
|
int tmpGrenade[GRENADE_TYPE_COUNT + 1];
|
|
int tmpSpecial[SPECIAL_COUNT + 1];
|
|
qboolean homing_state = client->pers.homing_state;
|
|
int laseron = client->pers.laseron;
|
|
int feign = client->pers.feign;
|
|
int autozoom = client->pers.autozoom;
|
|
int fastaim = client->pers.fastaim;
|
|
int nospam_level = client->pers.nospam_level;
|
|
qboolean autoconfig = client->pers.autoconfig;
|
|
int player_class = client->pers.player_class;
|
|
int next_player_class = client->pers.next_player_class;
|
|
edict_t *friend_ent;
|
|
int hasfriends = client->pers.hasfriends;
|
|
int isbot = client->pers.i_am_a_bot;
|
|
|
|
friend_ent = client->pers.friend_ent[0];
|
|
|
|
//Save grenade and special counts
|
|
for (i=1; i <= GRENADE_TYPE_COUNT; ++i)
|
|
{
|
|
tmpGrenade[i] = client->pers.active_grenades[i];
|
|
}
|
|
|
|
for (i=1; i <= SPECIAL_COUNT; ++i)
|
|
tmpSpecial[i] = client->pers.active_special[i];
|
|
|
|
memset (&client->pers, 0, sizeof(client->pers));
|
|
|
|
//Restore grenade and special counts
|
|
for (i=1; i <= GRENADE_TYPE_COUNT; ++i)
|
|
{
|
|
if (tmpGrenade[i] < 0) tmpGrenade[i] = 0;
|
|
client->pers.active_grenades[i] = tmpGrenade[i];
|
|
}
|
|
|
|
for (i=1; i <= SPECIAL_COUNT; ++i)
|
|
client->pers.active_special[i] = tmpSpecial[i];
|
|
|
|
|
|
item = FindItem("Blaster");
|
|
client->pers.selected_item = ITEM_INDEX(item);
|
|
client->pers.inventory[client->pers.selected_item] = 1;
|
|
|
|
client->pers.weapon = item;
|
|
//ZOID
|
|
client->pers.lastweapon = item;
|
|
//ZOID
|
|
|
|
//ERASER START
|
|
item = FindItem("Grapple");
|
|
// if (grapple->value || ctf->value)
|
|
if (ctf->value)
|
|
{//E
|
|
//ZOID
|
|
client->pers.inventory[ITEM_INDEX(item)] = 1;//NORMAL LINE
|
|
//ZOID
|
|
}//E
|
|
else//E
|
|
{//E
|
|
client->pers.inventory[ITEM_INDEX(item)] = 0;//E
|
|
}
|
|
//ERASER END GRAPPLE TROUBLE HERE FIXME ACRID
|
|
|
|
client->pers.i_am_a_bot = isbot;
|
|
|
|
client->pers.health = 100;
|
|
client->pers.max_health = 100;
|
|
|
|
client->pers.connected = true;
|
|
client->pers.friend_ent[0] = friend_ent;
|
|
|
|
// WF & CCH: reset homing_state
|
|
client->pers.HasVoted = HasVoted;
|
|
|
|
client->pers.homing_state = homing_state;
|
|
client->pers.laseron = laseron;
|
|
client->pers.feign = feign;
|
|
client->pers.autozoom = autozoom;
|
|
client->pers.fastaim = fastaim;
|
|
client->pers.nospam_level = nospam_level;
|
|
client->pers.autoconfig = autoconfig;
|
|
client->pers.player_class = player_class;
|
|
client->pers.next_player_class = next_player_class;
|
|
|
|
client->pers.hasfriends = hasfriends;
|
|
|
|
// Initialize grenade type
|
|
client->pers.grenade_num = 1;
|
|
client->pers.grenade_type = GRENADE_TYPE_NORMAL;
|
|
if (((int)wfflags->value & WF_NO_PLAYER_CLASSES) == 0)
|
|
client->pers.grenade_type = client->grenade_type1;
|
|
|
|
// Scanner
|
|
// ClearScanner(client);
|
|
|
|
// Scanner
|
|
// CompassOff(client);//5/99 compass
|
|
|
|
}
|
|
|
|
|
|
qboolean is_bot=false;//ERASER
|
|
void InitClientResp (gclient_t *client, edict_t *ent)
|
|
{
|
|
// int i;
|
|
int ctf_team;
|
|
|
|
//K2:begin
|
|
qboolean inServer = client->resp.inServer;
|
|
//K2:End 3/99 botcam
|
|
|
|
ctf_team = client->resp.ctf_team;
|
|
|
|
memset (&client->resp, 0, sizeof(client->resp));
|
|
|
|
client->resp.ctf_team = ctf_team;
|
|
|
|
//K2:begin
|
|
client->resp.inServer = inServer;
|
|
//K2:End 3/99 botcam
|
|
|
|
client->resp.enterframe = level.framenum;
|
|
client->resp.coop_respawn = client->pers;
|
|
|
|
//ZOID
|
|
// if (ctf->value && (client->resp.ctf_team < CTF_TEAM1))
|
|
if (client->resp.ctf_team < CTF_TEAM1)
|
|
CTFAssignTeam(client, is_bot);
|
|
//ZOID
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SaveClientData
|
|
|
|
Some information that should be persistant, like health,
|
|
is still stored in the edict structure, so it needs to
|
|
be mirrored out to the client structure before all the
|
|
edicts are wiped.
|
|
==================
|
|
*/
|
|
void SaveClientData (void)
|
|
{
|
|
int i;
|
|
edict_t *ent;
|
|
|
|
for (i=0 ; i<game.maxclients ; i++)
|
|
{
|
|
ent = &g_edicts[1+i];
|
|
if (!ent->inuse)
|
|
continue;
|
|
game.clients[i].pers.health = ent->health;
|
|
game.clients[i].pers.max_health = ent->max_health;
|
|
game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR));
|
|
if (coop->value)
|
|
game.clients[i].pers.score = ent->client->resp.score;
|
|
}
|
|
}
|
|
|
|
void FetchClientEntData (edict_t *ent)
|
|
{
|
|
ent->health = ent->client->pers.health;
|
|
ent->max_health = ent->client->pers.max_health;
|
|
ent->flags |= ent->client->pers.savedFlags;
|
|
if (coop->value)
|
|
ent->client->resp.score = ent->client->pers.score;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=======================================================================
|
|
|
|
SelectSpawnPoint
|
|
|
|
=======================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
PlayersRangeFromSpot
|
|
|
|
Returns the distance to the nearest player from the given spot
|
|
================
|
|
*/
|
|
float PlayersRangeFromSpot (edict_t *spot)
|
|
{
|
|
edict_t *player;
|
|
float bestplayerdistance;
|
|
vec3_t v;
|
|
int n;
|
|
float playerdistance;
|
|
|
|
|
|
bestplayerdistance = 9999999;
|
|
|
|
/*
|
|
for (n = 0; n < num_players; n++)//WF24 LINE IS for (n = 1; n <= maxclients->value; n++)
|
|
{
|
|
player = &g_edicts[n];
|
|
*/
|
|
|
|
for (n = 0; n < num_players; n++)//WF24 LINE IS for (n = 1; n <= maxclients->value; n++)
|
|
{
|
|
player = players[n];//ERASER ,WF USES LINE ABOVE THIS
|
|
|
|
if (!player->inuse)
|
|
continue;
|
|
|
|
if (player->health <= 0)
|
|
continue;
|
|
|
|
VectorSubtract (spot->s.origin, player->s.origin, v);
|
|
playerdistance = VectorLength (v);
|
|
|
|
if (playerdistance < bestplayerdistance)
|
|
bestplayerdistance = playerdistance;
|
|
}
|
|
|
|
return bestplayerdistance;
|
|
}
|
|
|
|
/*
|
|
================
|
|
SelectRandomDeathmatchSpawnPoint
|
|
|
|
go to a random point, but NOT the two points closest
|
|
to other players
|
|
================
|
|
*/
|
|
edict_t *SelectRandomDeathmatchSpawnPoint (void)
|
|
{
|
|
edict_t *spot, *spot1, *spot2;
|
|
int count = 0, spot_num;//ERASER
|
|
int selection;
|
|
float range, range1, range2;
|
|
|
|
spot = NULL;
|
|
range1 = range2 = 99999;
|
|
spot1 = spot2 = NULL;
|
|
//WF USES THIS CO LINE
|
|
// while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
|
|
while (count < num_dm_spots)
|
|
{
|
|
spot = dm_spots[count];//ERASER
|
|
|
|
count++;
|
|
range = PlayersRangeFromSpot(spot);
|
|
if (range < range1)
|
|
{
|
|
range1 = range;
|
|
spot1 = spot;
|
|
}
|
|
else if (range < range2)
|
|
{
|
|
range2 = range;
|
|
spot2 = spot;
|
|
}
|
|
}
|
|
|
|
if (!count)
|
|
return NULL;
|
|
|
|
if (count <= 2)
|
|
{
|
|
spot1 = spot2 = NULL;
|
|
}
|
|
else
|
|
count -= 2;
|
|
|
|
selection = rand() % count;
|
|
|
|
spot = NULL;
|
|
spot_num = 0;//ERASER
|
|
do
|
|
{
|
|
//WF24 USES spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
|
|
spot = dm_spots[spot_num++];//ERASER
|
|
if (spot == spot1 || spot == spot2)
|
|
selection++;
|
|
} while(selection--);
|
|
|
|
return spot;
|
|
}
|
|
|
|
/*
|
|
================
|
|
SelectFarthestDeathmatchSpawnPoint
|
|
|
|
================
|
|
*/
|
|
edict_t *SelectFarthestDeathmatchSpawnPoint (void)
|
|
{
|
|
edict_t *bestspot;
|
|
float bestdistance, bestplayerdistance;
|
|
edict_t *spot;
|
|
int spot_num;//ERASER
|
|
|
|
|
|
spot = NULL;
|
|
bestspot = NULL;
|
|
bestdistance = 0;
|
|
spot_num = 0;//ERASER
|
|
//WF24 USES while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
|
|
while (spot_num < num_dm_spots)//ERASER
|
|
{
|
|
spot = dm_spots[spot_num++];//ERASER
|
|
|
|
bestplayerdistance = PlayersRangeFromSpot (spot);
|
|
|
|
if (bestplayerdistance > bestdistance)
|
|
{
|
|
bestspot = spot;
|
|
bestdistance = bestplayerdistance;
|
|
}
|
|
}
|
|
|
|
if (bestspot)
|
|
{
|
|
return bestspot;
|
|
}
|
|
|
|
// if there is a player just spawned on each and every start spot
|
|
// we have no choice to turn one into a telefrag meltdown
|
|
//WF USES spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
|
|
spot = dm_spots[0];//ERASER
|
|
|
|
return spot;
|
|
}
|
|
|
|
edict_t *SelectDeathmatchSpawnPoint (void)
|
|
{
|
|
if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
|
|
return SelectFarthestDeathmatchSpawnPoint ();
|
|
else
|
|
return SelectRandomDeathmatchSpawnPoint ();
|
|
}
|
|
|
|
|
|
edict_t *SelectCoopSpawnPoint (edict_t *ent)
|
|
{
|
|
int index;
|
|
edict_t *spot = NULL;
|
|
char *target;
|
|
|
|
index = ent->client - game.clients;
|
|
|
|
// player 0 starts in normal player spawn point
|
|
if (!index)
|
|
return NULL;
|
|
|
|
spot = NULL;
|
|
|
|
// assume there are four coop spots at each spawnpoint
|
|
while (1)
|
|
{
|
|
spot = G_Find (spot, FOFS(classname), "info_player_coop");
|
|
if (!spot)
|
|
return NULL; // we didn't have enough...
|
|
|
|
target = spot->targetname;
|
|
if (!target)
|
|
target = "";
|
|
if ( Q_stricmp(game.spawnpoint, target) == 0 )
|
|
{ // this is a coop spawn point for one of the clients here
|
|
index--;
|
|
if (!index)
|
|
return spot; // this is it
|
|
}
|
|
}
|
|
|
|
|
|
return spot;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
SelectSpawnPoint
|
|
|
|
Chooses a player start, deathmatch start, coop start, etc
|
|
============
|
|
*/
|
|
void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
|
|
{
|
|
edict_t *spot = NULL;
|
|
|
|
if (deathmatch->value)
|
|
//ZOID
|
|
if (ctf->value)
|
|
spot = SelectCTFSpawnPoint(ent);
|
|
else
|
|
//ZOID
|
|
spot = SelectDeathmatchSpawnPoint ();
|
|
else if (coop->value)
|
|
spot = SelectCoopSpawnPoint (ent);
|
|
|
|
// find a single player start spot
|
|
if (!spot)
|
|
{
|
|
// gi.dprintf("DEBUG - No CTF Spawn Point.\n");
|
|
while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
|
|
{
|
|
if (!game.spawnpoint[0] && !spot->targetname)
|
|
break;
|
|
|
|
if (!game.spawnpoint[0] || !spot->targetname)
|
|
continue;
|
|
|
|
if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
|
|
break;
|
|
}
|
|
|
|
if (!spot)
|
|
{
|
|
// gi.dprintf("DEBUG - No Spawn Point Without Target.\n");
|
|
if (!game.spawnpoint[0])
|
|
{ // there wasn't a spawnpoint without a target, so use any
|
|
spot = G_Find (spot, FOFS(classname), "info_player_start");
|
|
}
|
|
if (!spot)
|
|
gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
|
|
}
|
|
}
|
|
|
|
VectorCopy (spot->s.origin, origin);
|
|
origin[2] += 9;
|
|
VectorCopy (spot->s.angles, angles);
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
|
|
void InitBodyQue (void)
|
|
{
|
|
int i;
|
|
edict_t *ent;
|
|
|
|
level.body_que = 0;
|
|
for (i=0; i<BODY_QUEUE_SIZE ; i++)
|
|
{
|
|
ent = G_Spawn();
|
|
ent->classname = "bodyque";
|
|
}
|
|
}
|
|
|
|
//ERASER START
|
|
void Body_droptofloor(edict_t *ent)
|
|
{
|
|
trace_t tr;
|
|
vec3_t dest;
|
|
float *v;
|
|
|
|
v = tv(-15,-15,-24);
|
|
VectorCopy (v, ent->mins);
|
|
v = tv(15,15,15);
|
|
VectorCopy (v, ent->maxs);
|
|
|
|
v = tv(0,0,-128);
|
|
VectorAdd (ent->s.origin, v, dest);
|
|
|
|
ent->s.origin[2] += 32;
|
|
|
|
tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
|
|
|
|
VectorCopy(tr.endpos, ent->s.origin);
|
|
|
|
gi.linkentity(ent);
|
|
|
|
if (tr.ent)
|
|
ent->nextthink = level.time + 0.1;
|
|
}
|
|
//ERASER END
|
|
|
|
void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
int n;
|
|
|
|
if (self->health < -40)
|
|
{
|
|
gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
|
for (n= 0; n < 4; n++)
|
|
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
|
self->s.origin[2] -= 48;
|
|
ThrowClientHead (self, damage);
|
|
self->takedamage = DAMAGE_NO;
|
|
}
|
|
}
|
|
|
|
void CopyToBodyQue (edict_t *ent)
|
|
{
|
|
edict_t *body;
|
|
|
|
|
|
// grab a body que and cycle to the next one
|
|
body = &g_edicts[(int)maxclients->value + level.body_que + 1];
|
|
level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
|
|
|
|
// FIXME: send an effect on the removed body
|
|
|
|
gi.unlinkentity (ent);
|
|
// body->frozenbody =1;//acrid 3/99 this may be a solution if theres problems between the ent pointer and body
|
|
gi.unlinkentity (body);
|
|
body->s = ent->s;
|
|
body->s.number = body - g_edicts;
|
|
|
|
body->svflags = ent->svflags;
|
|
VectorCopy (ent->mins, body->mins);
|
|
VectorCopy (ent->maxs, body->maxs);
|
|
VectorCopy (ent->absmin, body->absmin);
|
|
VectorCopy (ent->absmax, body->absmax);
|
|
VectorCopy (ent->size, body->size);
|
|
body->solid = ent->solid;
|
|
body->clipmask = ent->clipmask;
|
|
body->owner = ent->owner;
|
|
// body->s.effects = 0;
|
|
// body->s.renderfx = 0;
|
|
body->movetype = ent->movetype;
|
|
|
|
body->die = body_die;
|
|
body->takedamage = DAMAGE_YES;
|
|
|
|
gi.linkentity (body);
|
|
}
|
|
|
|
void WFCopyToBodyQue (edict_t *ent)
|
|
{
|
|
edict_t *body;
|
|
char modelname[64];//acrid
|
|
int classnum;//acrid
|
|
|
|
// grab a body que and cycle to the next one
|
|
body = &g_edicts[(int)maxclients->value + level.body_que + 1];
|
|
level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
|
|
|
|
// FIXME: send an effect on the removed body
|
|
|
|
gi.unlinkentity (ent);
|
|
|
|
gi.unlinkentity (body);
|
|
|
|
|
|
//new skin fix code
|
|
body->wf_team = ent->client->resp.ctf_team;
|
|
body->model = ent->model;//needed????? 3/99
|
|
classnum = ent->client->pers.player_class;
|
|
sprintf(modelname, "wfactory/models/decoys/%s/tris.md2", classinfo[classnum].model_name);
|
|
body->s.modelindex = gi.modelindex (modelname);
|
|
body->s.skinnum = classinfo[classnum].decoyskin;
|
|
if (body->wf_team == CTF_TEAM1) //team 1
|
|
{
|
|
++body->s.skinnum;
|
|
}
|
|
else
|
|
{
|
|
//team 2
|
|
}
|
|
//end new skin fix
|
|
|
|
// body->s = ent->s;
|
|
VectorCopy(ent->s.origin, body->s.origin);//acrid
|
|
VectorCopy(ent->s.angles, body->s.angles);//acrid
|
|
VectorCopy(ent->s.old_origin, body->s.old_origin);//acrid
|
|
body->s.frame = ent->s.frame;//acrid
|
|
body->s.number = body - g_edicts;
|
|
body->svflags = ent->svflags;
|
|
VectorCopy (ent->mins, body->mins);
|
|
VectorCopy (ent->maxs, body->maxs);
|
|
VectorCopy (ent->absmin, body->absmin);
|
|
VectorCopy (ent->absmax, body->absmax);
|
|
VectorCopy (ent->size, body->size);
|
|
body->solid = ent->solid;
|
|
body->s.event = ent->s.event;//acrid
|
|
body->clipmask = ent->clipmask;
|
|
body->owner = ent->owner;
|
|
body->s.effects = ent->s.effects;
|
|
body->s.renderfx = ent->s.renderfx;
|
|
|
|
//If they were carrying the flag, clear the glow - GREGG
|
|
if (body->s.effects & (EF_FLAG1 | EF_FLAG2))
|
|
{
|
|
body->s.effects = 0;
|
|
body->s.renderfx = 0;
|
|
}
|
|
|
|
|
|
body->movetype = ent->movetype;
|
|
body->die = body_die;
|
|
body->takedamage = DAMAGE_YES;
|
|
|
|
gi.linkentity (body);
|
|
}
|
|
|
|
void respawn (edict_t *self)
|
|
{
|
|
if (deathmatch->value || coop->value)
|
|
{
|
|
if (((int)wfflags->value & WF_NO_PLAYER_CLASSES) == 0)
|
|
{ //for stopping instant gib creating a extra body//may need !self->frozenbody
|
|
if (self->health > -40 && !self->bot_client)
|
|
WFCopyToBodyQue (self);
|
|
//use normal que if instant gib
|
|
else
|
|
CopyToBodyQue (self);
|
|
}
|
|
//if wfflags use normal que
|
|
else
|
|
{
|
|
CopyToBodyQue (self);
|
|
}
|
|
|
|
|
|
|
|
PutClientInServer (self);
|
|
|
|
// add a teleportation effect
|
|
self->s.event = EV_PLAYER_TELEPORT;
|
|
|
|
// hold in place briefly
|
|
self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
|
|
self->client->ps.pmove.pm_time = 14;
|
|
|
|
self->client->respawn_time = level.time;
|
|
|
|
return;
|
|
}
|
|
|
|
// restart the entire server
|
|
gi.AddCommandString ("menu_loadgame\n");
|
|
}
|
|
|
|
//==============================================================
|
|
|
|
// WF: Weapons Factory user setup */
|
|
static void WFClientSetup (edict_t *ent)
|
|
{
|
|
int i;
|
|
|
|
//Set aliases required for mod
|
|
stuffcmd(ent,"alias +thrust \"cmd thrust on\"\n");
|
|
stuffcmd(ent,"alias -thrust \"cmd thrust off\"\n");
|
|
stuffcmd(ent,"alias +grapple \"+use\"\n");//newgrap 4/99
|
|
stuffcmd(ent,"alias -grapple \"-use\"\n");//newgrap 4/99
|
|
stuffcmd(ent,"alias +rzoom \"fov 30;+mlook\"\n");
|
|
stuffcmd(ent,"alias -rzoom \"fov 90;-mlook\"\n");
|
|
//stuffcmd(ent,"alias wfhelp \"cmd wfhelp\"\n");
|
|
//stuffcmd(ent,"alias vote \"cmd vote\"\n");
|
|
|
|
//FORCE SOME BINDS
|
|
// stuffcmd(ent,"bind \";\" cmd special\n");
|
|
// stuffcmd(ent,"bind v +thrust\n");
|
|
// stuffcmd(ent,"bind b cmd grenade\n");
|
|
|
|
//note - removed mlook from rzoom command
|
|
|
|
//Clear grenade and special item limits
|
|
for (i=1; i <= GRENADE_TYPE_COUNT; ++i)
|
|
ent->client->pers.active_grenades[i] = 0;
|
|
|
|
for (i=1; i <= SPECIAL_COUNT; ++i)
|
|
ent->client->pers.active_special[i] = 0;
|
|
|
|
}
|
|
//WF
|
|
|
|
/*
|
|
===========
|
|
PutClientInServer
|
|
|
|
Called when a player connects to a server or respawns in
|
|
a deathmatch.
|
|
============
|
|
*/
|
|
|
|
void ShowGun(edict_t *ent);//ERASER
|
|
|
|
void PutClientInServer (edict_t *ent)
|
|
{
|
|
vec3_t mins = {-16, -16, -24};
|
|
vec3_t maxs = {16, 16, 32};
|
|
int index;
|
|
vec3_t spawn_origin, spawn_angles;
|
|
gclient_t *client;
|
|
int i;
|
|
client_persistant_t saved;
|
|
client_respawn_t resp;
|
|
//ERASER START
|
|
bot_team_t *team;
|
|
lag_trail_t *lag_trail;
|
|
lag_trail_t *lag_angles;
|
|
float latency;
|
|
|
|
if (!the_client && !ent->bot_client && (num_players <= 1))
|
|
the_client = ent;
|
|
|
|
// find a spawn point
|
|
// do it before setting health back up, so farthest
|
|
// ranging doesn't count this client
|
|
SelectSpawnPoint (ent, spawn_origin, spawn_angles);
|
|
|
|
index = ent-g_edicts-1;
|
|
client = ent->client;
|
|
|
|
HealPlayer(ent);
|
|
|
|
//Did class change?
|
|
if (ent->client->pers.player_class != ent->client->pers.next_player_class)
|
|
{
|
|
//Remove all their old stuff lying around
|
|
// gi.dprintf("Class changed-removing stuff. Old class=%d, New Class=%d\n",ent->client->pers.player_class, ent->client->pers.next_player_class);
|
|
WFPlayer_ChangeClassTeam(ent);
|
|
}
|
|
|
|
ent->client->pers.player_class = ent->client->pers.next_player_class;
|
|
|
|
// deathmatch wipes most client data every spawn
|
|
if (deathmatch->value)
|
|
{
|
|
char userinfo[MAX_INFO_STRING];
|
|
|
|
resp = client->resp;
|
|
memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
|
|
InitClientPersistant (client);
|
|
ClientUserinfoChanged (ent, userinfo);
|
|
}
|
|
else if (coop->value)
|
|
{
|
|
int n;
|
|
char userinfo[MAX_INFO_STRING];
|
|
|
|
resp = client->resp;
|
|
memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
|
|
// this is kind of ugly, but it's how we want to handle keys in coop
|
|
for (n = 0; n < MAX_ITEMS; n++)
|
|
{
|
|
if (itemlist[n].flags & IT_KEY)
|
|
resp.coop_respawn.inventory[n] = client->pers.inventory[n];
|
|
}
|
|
client->pers = resp.coop_respawn;
|
|
ClientUserinfoChanged (ent, userinfo);
|
|
if (resp.score > client->pers.score)
|
|
client->pers.score = resp.score;
|
|
}
|
|
else
|
|
{
|
|
memset (&resp, 0, sizeof(resp));
|
|
}
|
|
|
|
// clear everything but the persistant data
|
|
saved = client->pers;
|
|
//ERASER START
|
|
team = client->team;
|
|
lag_trail = client->lag_trail;
|
|
lag_angles = client->lag_angles;
|
|
latency = client->latency;
|
|
//ERASER END
|
|
memset (client, 0, sizeof(*client));
|
|
|
|
client->pers = saved;
|
|
client->resp = resp;
|
|
//ERASER START
|
|
client->team = team;
|
|
client->lag_trail = lag_trail;
|
|
client->lag_angles = lag_angles;
|
|
client->latency = latency;
|
|
if (client->pers.health <= 0)
|
|
InitClientPersistant(client);
|
|
//ERASER END
|
|
// copy some data from the client to the entity
|
|
FetchClientEntData (ent);
|
|
|
|
if (!ent->wf_team) ent->wf_team = client->resp.ctf_team;
|
|
|
|
// clear entity values
|
|
ent->groundentity = NULL;
|
|
if (!ent->bot_client)//ERASER
|
|
ent->client = &game.clients[index];
|
|
ent->takedamage = DAMAGE_AIM;
|
|
ent->movetype = MOVETYPE_WALK;
|
|
ent->viewheight = 22;
|
|
ent->inuse = true;
|
|
ent->classname = "player";
|
|
ent->mass = 200;
|
|
ent->solid = SOLID_BBOX;
|
|
ent->deadflag = DEAD_NO;
|
|
ent->air_finished = level.time + 12;
|
|
ent->clipmask = MASK_PLAYERSOLID;
|
|
ent->model = "players/male/tris.md2";
|
|
|
|
//ERASER START
|
|
if (!ent->bot_client)
|
|
{
|
|
ent->pain = player_pain;
|
|
ent->die = player_die;
|
|
}
|
|
//ERASER END MIDDLE 2 LINES NORMAL WF24 CODE
|
|
|
|
ent->waterlevel = 0;
|
|
ent->watertype = 0;
|
|
ent->flags &= ~FL_NO_KNOCKBACK;
|
|
ent->svflags &= ~SVF_DEADMONSTER;
|
|
// acrid 3/99 start unfrozen
|
|
ent->frozen = 0;
|
|
ent->frozenbody = 0;
|
|
VectorCopy (mins, ent->mins);
|
|
VectorCopy (maxs, ent->maxs);
|
|
VectorClear (ent->velocity);
|
|
|
|
// clear playerstate values
|
|
memset (&ent->client->ps, 0, sizeof(client->ps));
|
|
|
|
client->ps.pmove.origin[0] = spawn_origin[0]*8;
|
|
client->ps.pmove.origin[1] = spawn_origin[1]*8;
|
|
client->ps.pmove.origin[2] = spawn_origin[2]*8;
|
|
//ZOID
|
|
client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
|
|
//ZOID
|
|
|
|
if (!ent->bot_client)//ERASER
|
|
{//E
|
|
if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
|
|
{
|
|
client->ps.fov = 90;
|
|
}
|
|
else
|
|
{
|
|
client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
|
|
if (client->ps.fov < 1)
|
|
client->ps.fov = 90;
|
|
else if (client->ps.fov > 160)
|
|
client->ps.fov = 160;
|
|
}
|
|
|
|
client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
|
|
}
|
|
|
|
|
|
client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
|
|
|
|
// clear entity state values
|
|
ent->s.effects = 0;
|
|
// sknum is player num and weapon number
|
|
// weapon number will be added in changeweapon
|
|
ent->s.skinnum = ent - g_edicts - 1;
|
|
ent->s.modelindex = 255; // will use the skin specified model
|
|
ent->s.modelindex2 = 255; // custom gun model
|
|
if (ent->bot_client)
|
|
{
|
|
ShowGun(ent);
|
|
}
|
|
ent->s.frame = 0;
|
|
VectorCopy (spawn_origin, ent->s.origin);
|
|
ent->s.origin[2] += 1; // make sure off ground
|
|
VectorCopy (ent->s.origin, ent->s.old_origin);
|
|
|
|
// set the delta angle
|
|
for (i=0 ; i<3 ; i++)
|
|
client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
|
|
|
|
ent->s.angles[PITCH] = 0;
|
|
ent->s.angles[YAW] = spawn_angles[YAW];
|
|
ent->s.angles[ROLL] = 0;
|
|
VectorCopy (ent->s.angles, client->ps.viewangles);
|
|
VectorCopy (ent->s.angles, client->v_angle);
|
|
|
|
//WF - for flash grenades
|
|
client->blindBase = 0;
|
|
client->blindTime = 0;
|
|
|
|
//Clear disguise stuff
|
|
ent->disguised = 0;
|
|
ent->disguising = 0;
|
|
ent->disguisetime = 0;
|
|
ent->disguisingteam = 0;
|
|
ent->disguisedteam = 0;
|
|
ent->disguiseshots = 0;
|
|
|
|
lasersight_off (ent);
|
|
|
|
//Stop any disease
|
|
ent->disease = 0;
|
|
ent->cantmove = 0;
|
|
ent->Slower = 0;
|
|
|
|
ent->client->weapon_damage = DAMAGE_BLASTER; //blaster is startup weapon
|
|
|
|
//WF
|
|
//set the team of the entity
|
|
ent->wf_team = client->resp.ctf_team;
|
|
|
|
//set respawn protection time
|
|
ent->client->protecttime = level.time + RESPAWN_PROTECT_TIME;
|
|
|
|
//Set player classes
|
|
|
|
//Fixup class if team is set but there is no next_player_class
|
|
/*
|
|
if ((client->resp.ctf_team > 0) && (ent->client->pers.next_player_class == 0))
|
|
{
|
|
//Can we use current setting?
|
|
if (ent->client->pers.player_class)
|
|
{
|
|
ent->client->pers.next_player_class = ent->client->pers.player_class;
|
|
}
|
|
//Just assign them to the first class if all else fails
|
|
else
|
|
{
|
|
ent->client->pers.next_player_class = 1;
|
|
}
|
|
}
|
|
*/
|
|
/* Old position of this code
|
|
//Did class change?
|
|
if (ent->client->pers.player_class != ent->client->pers.next_player_class)
|
|
{
|
|
//Remove all their old stuff lying around
|
|
// gi.dprintf("Class changed-removing stuff. Old class=%d, New Class=%d\n",ent->client->pers.player_class, ent->client->pers.next_player_class);
|
|
WFPlayer_ChangeClassTeam(ent);
|
|
}
|
|
|
|
ent->client->pers.player_class = ent->client->pers.next_player_class;
|
|
|
|
*/
|
|
if ((((int)wfflags->value & WF_NO_PLAYER_CLASSES) == 0) && (ent->client->pers.player_class))
|
|
{
|
|
// gi.dprintf("Player Classes Used\n");
|
|
wf_InitPlayerClass(ent->client);
|
|
}
|
|
// else if ((int)wfflags->value & WF_RESPAWN_ARMED)
|
|
// {
|
|
// ent->client->pers.next_player_class = CLASS_SUPERMAN;
|
|
// ent->client->pers.player_class = CLASS_SUPERMAN;
|
|
// wf_InitPlayerClass(ent->client);
|
|
// }
|
|
else
|
|
{
|
|
ent->client->pers.next_player_class = 0;
|
|
ent->client->pers.player_class = 0;
|
|
}
|
|
Cmd_ShowClass(ent);
|
|
|
|
//Pick the best weapon for the class
|
|
NoAmmoWeaponChange (ent);
|
|
|
|
|
|
|
|
//ZOID
|
|
if (CTFStartClient(ent))
|
|
return;
|
|
//ZOID
|
|
|
|
|
|
if (!KillBox (ent))
|
|
{ // could't spawn in?
|
|
}
|
|
|
|
gi.linkentity (ent);
|
|
//ERASER START
|
|
if (!ent->map)
|
|
ent->map = G_CopyString(ent->client->pers.netname);
|
|
//ERASER END
|
|
|
|
// force the current weapon up
|
|
client->newweapon = client->pers.weapon;
|
|
if (!ent->bot_client)//ERASER
|
|
ChangeWeapon (ent);
|
|
|
|
|
|
//If this is a zbot player, mess them up.
|
|
if (ent->client->pers.i_am_a_bot)
|
|
I_AM_A_ZBOT(ent);
|
|
|
|
//ERASER START
|
|
ent->last_max_z = 32;
|
|
ent->jump_ent = NULL;
|
|
ent->duck_ent = NULL;
|
|
ent->last_trail_dropped = NULL;
|
|
|
|
if (!ent->bot_client)
|
|
{
|
|
if (ent->client->lag_trail)
|
|
{
|
|
// init the lag_trail
|
|
for (i=0; i<10; i++)
|
|
{
|
|
VectorCopy(ent->s.origin, (*ent->client->lag_trail)[i]);
|
|
VectorCopy(ent->client->v_angle, (*ent->client->lag_angles)[i]);
|
|
}
|
|
}
|
|
|
|
if ((bot_calc_nodes->value) && (trail[0]->timestamp))
|
|
{ // find a trail node to start checking from
|
|
if ((i = ClosestNodeToEnt(ent, false, true)) > -1)
|
|
{
|
|
ent->last_trail_dropped = trail[i];
|
|
}
|
|
}
|
|
}
|
|
//ERASER END
|
|
}
|
|
|
|
/* Anti-ZBot Support Code */
|
|
void wf_strdate(char *s);
|
|
void wf_strtime(char *s);
|
|
|
|
void zbotFileOpen( )
|
|
{
|
|
char path[100];
|
|
|
|
zbotfile = NULL;
|
|
|
|
//Is zbot detection on?
|
|
if (((int)wfflags->value & WF_ZBOT_DETECT) == 0)
|
|
return;
|
|
|
|
strcpy(path, gamedir->string);
|
|
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
strcat(path,"\\zbot.log");
|
|
#else
|
|
strcat(path,"/zbot.log");
|
|
#endif
|
|
|
|
if((zbotfile = fopen(path,"a")) != NULL)
|
|
{
|
|
fprintf(zbotfile,"\nSERVER RESTART\n");
|
|
gi.dprintf("ZBot Protection Enabled\n");
|
|
}
|
|
}
|
|
|
|
void zbotLogAttack(edict_t *ent, int type)
|
|
{
|
|
char tDate[50];
|
|
char tTime[50];
|
|
char *stype;
|
|
|
|
if (type == 2) stype = "@zbot";
|
|
else if (type == 3) stype = "#zbot";
|
|
else stype = "!zbot";
|
|
|
|
if (!zbotfile) return;
|
|
|
|
//value = Info_ValueForKey (userinfo, "ip");
|
|
wf_strdate( tDate );
|
|
wf_strtime( tTime );
|
|
|
|
fprintf(zbotfile,"%s %s: Name = %s, Type = %s\n",
|
|
tDate, tTime, ent->client->pers.netname, stype);
|
|
fflush(zbotfile);
|
|
|
|
}
|
|
|
|
void zbotFileClose()
|
|
{
|
|
if (zbotfile) fclose(zbotfile);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
ClientBeginDeathmatch
|
|
|
|
A client has just connected to the server in
|
|
deathmatch mode, so clear everything out before starting them.
|
|
=====================
|
|
*/
|
|
void ClientBeginDeathmatch (edict_t *ent)
|
|
{
|
|
|
|
G_InitEdict (ent);
|
|
//ERASER START
|
|
if (ctf->value)
|
|
ent->client->team = NULL;
|
|
//ERASER END
|
|
InitClientResp (ent->client, ent);//WF34 ADDED ENT
|
|
if (!ent->wf_team) ent->wf_team = ent->client->resp.ctf_team;
|
|
|
|
|
|
// locate ent at a spawn point
|
|
PutClientInServer (ent);
|
|
ent->client->pers.autozoom = 1; //default sniper rifle to auto-zoom
|
|
//WF34 START
|
|
// -- ANTI-ZBOT STARTS -- //
|
|
//ent->client->resp.bot_start = level.time + 5 + (rand() % 5);
|
|
//ent->client->resp.bot_end = 0;
|
|
//ent->client->resp.bot_end2 = 0;
|
|
//ent->client->resp.bot_end3 = 0;
|
|
|
|
//Send these commands. User must respond back with all 3
|
|
// between 5 and 9 seconds later.
|
|
/* C.O. For Lithium Test
|
|
if ((int)wfflags->value & WF_ZBOT_DETECT)
|
|
{
|
|
stuffcmd(ent, "!zbot\n");
|
|
ent->client->resp.bot_end = level.time + 4;
|
|
stuffcmd(ent, "@zbot\n");
|
|
ent->client->resp.bot_end2 = level.time + 4;
|
|
stuffcmd(ent, "#zbot\n");
|
|
ent->client->resp.bot_end3 = level.time + 4;
|
|
|
|
ent->client->resp.bot_retries = 0;
|
|
ent->client->resp.bot_retries = 2;
|
|
ent->client->resp.bot_retries = 3;
|
|
}
|
|
C.O. For Lithium Test */
|
|
// -- ANTI-ZBOT ENDS -- //
|
|
|
|
//Enforce cl_forwardspeed and cl_sidespeed 5/99 acrid
|
|
if (!ent->bot_client)
|
|
{
|
|
stuffcmd(ent,"cl_forwardspeed 200\n");
|
|
stuffcmd(ent,"cl_sidespeed 200\n");
|
|
}
|
|
|
|
//ERASER START
|
|
///Q2 Camera Begin
|
|
EntityListAdd(ent);
|
|
///Q2 Camera End
|
|
//ERASER END
|
|
//WF34 SE
|
|
// Voting
|
|
ent->client->pers.HasVoted = false;
|
|
|
|
// Homing
|
|
ent->client->pers.homing_state = 1;
|
|
|
|
// Feign
|
|
ent->client->pers.feign = 0;
|
|
|
|
if (level.intermissiontime)
|
|
{
|
|
MoveClientToIntermission (ent);
|
|
}
|
|
else
|
|
{//WF34 E
|
|
// send effect
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_LOGIN);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
}//WF34
|
|
my_bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
|
|
|
|
//WF24 S Setup user
|
|
WFClientSetup(ent);
|
|
sl_LogPlayerConnect( &gi, level, ent ); // StdLog - Mark Davies
|
|
//WF24 E
|
|
|
|
// make sure all view stuff is valid
|
|
ClientEndServerFrame (ent);
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
ClientBegin
|
|
|
|
called when a client has finished connecting, and is ready
|
|
to be placed into the game. This will happen every level load.
|
|
============
|
|
*///WF24 IS A BIT DIF
|
|
void botAddPlayer(edict_t *ent);
|
|
void ClientBegin (edict_t *ent)
|
|
{
|
|
int i;
|
|
|
|
//Check if player in cam mode botcam
|
|
if(ent->client->bIsCamera)
|
|
{
|
|
ent->client->bIsCamera = false;
|
|
botAddPlayer(ent);
|
|
}
|
|
|
|
ent->client = game.clients + (ent - g_edicts - 1);
|
|
//ERASER START
|
|
players[num_players++] = ent;
|
|
num_clients++;
|
|
|
|
|
|
if (!ctf->value)
|
|
{ // make sure grapple is removed if CTF has been disabled
|
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Grapple"))] = 0;
|
|
}
|
|
//ERASER END
|
|
if (deathmatch->value)
|
|
{
|
|
//ERASER START (TeT - commented out to prevent overflows)
|
|
// if (bot_show_connect_info->value)
|
|
// gi.centerprintf(ent, "\n\n=====================================\nThe Eraser Bot v%1.3f\nby Ryan Feltrin (aka Ridah)\n\nRead the readme.txt file\nlocated in the Eraser directory!\n\nVisit http://impact.frag.com/\nfor Eraser news\n\n-------------------------------------\n", ERASER_VERSION, maxclients->value);
|
|
//
|
|
// if (teamplay->value && !ctf->value)
|
|
// safe_cprintf(ent, PRINT_HIGH, "\n\n=====================================\nServer has enabled TEAMPLAY!\n\nType: \"cmd teams\" to see the list of teams\nType: \"cmd join <teamname>\" to join a team\n\n");
|
|
//ERASER END
|
|
ClientBeginDeathmatch (ent);
|
|
|
|
return;
|
|
}
|
|
|
|
// if there is already a body waiting for us (a loadgame), just
|
|
// take it, otherwise spawn one from scratch
|
|
if (ent->inuse == true)
|
|
{
|
|
// the client has cleared the client side viewangles upon
|
|
// connecting to the server, which is different than the
|
|
// state when the game is saved, so we need to compensate
|
|
// with deltaangles
|
|
for (i=0 ; i<3 ; i++)
|
|
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
|
|
}
|
|
else
|
|
{
|
|
// a spawn point will completely reinitialize the entity
|
|
// except for the persistant data that was initialized at
|
|
// ClientConnect() time
|
|
G_InitEdict (ent);
|
|
ent->classname = "player";
|
|
InitClientResp (ent->client, ent);//WF34 ADDED ENT
|
|
PutClientInServer (ent);
|
|
}
|
|
|
|
if (level.intermissiontime)
|
|
{
|
|
MoveClientToIntermission (ent);
|
|
}
|
|
else
|
|
{
|
|
// send effect if in a multiplayer game
|
|
if (game.maxclients > 1)
|
|
{
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_LOGIN);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
my_bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
|
|
}
|
|
}
|
|
//WF24 S Setup user
|
|
WFClientSetup(ent);
|
|
//WF24 E
|
|
|
|
// make sure all view stuff is valid
|
|
ClientEndServerFrame (ent);
|
|
}
|
|
|
|
/*
|
|
===========
|
|
ClientUserInfoChanged
|
|
|
|
called whenever the player updates a userinfo variable.
|
|
|
|
The game can override any of the settings in place
|
|
(forcing skins or names, etc) before copying it off.
|
|
============
|
|
*/
|
|
void ClientUserinfoChanged (edict_t *ent, char *userinfo)
|
|
{
|
|
char *s;
|
|
int playernum;
|
|
|
|
// check for malformed or illegal info strings
|
|
if (!Info_Validate(userinfo))
|
|
{
|
|
strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
|
|
}
|
|
|
|
// set name
|
|
s = Info_ValueForKey (userinfo, "name");
|
|
|
|
//Remove any "%" from name
|
|
wfFixName(s);
|
|
|
|
// start - mdavies
|
|
// Has the player got a name
|
|
if( strlen(ent->client->pers.netname) )
|
|
{
|
|
// has the name changed
|
|
if( strcmp( ent->client->pers.netname, s ) )
|
|
{
|
|
//int iTimeInSeconds = level.time;
|
|
|
|
// log player rename
|
|
sl_LogPlayerRename( &gi,
|
|
ent->client->pers.netname,
|
|
s);
|
|
}
|
|
}
|
|
// end - mdavies
|
|
strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
|
|
|
|
// set skin
|
|
s = Info_ValueForKey (userinfo, "skin");
|
|
|
|
playernum = ent-g_edicts-1;
|
|
|
|
// combine name and skin into a configstring
|
|
//ZOID
|
|
// if (ctf->value)
|
|
CTFAssignSkin(ent, s);
|
|
// else
|
|
//ZOID
|
|
// gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
|
|
|
|
// fov
|
|
if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
|
|
{
|
|
ent->client->ps.fov = 90;
|
|
}
|
|
else
|
|
{
|
|
ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
|
|
if (ent->client->ps.fov < 1)
|
|
ent->client->ps.fov = 90;
|
|
else if (ent->client->ps.fov > 160)
|
|
ent->client->ps.fov = 160;
|
|
}
|
|
|
|
// handedness
|
|
s = Info_ValueForKey (userinfo, "hand");
|
|
if (strlen(s))
|
|
{
|
|
ent->client->pers.hand = atoi(s);
|
|
}
|
|
|
|
// save off the userinfo in case we want to check something later
|
|
strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
ClientConnect
|
|
|
|
Called when a player begins connecting to the server.
|
|
The game can refuse entrance to a client by returning false.
|
|
If the client is allowed, the connection process will continue
|
|
and eventually get to ClientBegin()
|
|
Changing levels will NOT cause this to be called again, but
|
|
loadgames will.
|
|
============
|
|
*/
|
|
qboolean ClientConnect (edict_t *ent, char *userinfo)
|
|
{
|
|
char *value;
|
|
char *tmp;
|
|
int i;
|
|
|
|
// check to see if they are on the banned IP list
|
|
value = Info_ValueForKey (userinfo, "ip");
|
|
|
|
// drop port #
|
|
tmp = value;
|
|
if (!ent->bot_client)
|
|
/* while (tmp)
|
|
{
|
|
if (*(++tmp) == ':')
|
|
{
|
|
*tmp = (char) NULL;
|
|
break;
|
|
}
|
|
}*/ // mv: who thought of this
|
|
|
|
if (IsBanned(value))
|
|
{
|
|
gi.dprintf("*** Banned IP %s tried to connect!\n", value);
|
|
Info_SetValueForKey(userinfo, "rejmsg", "You are banned.");
|
|
return false;
|
|
}
|
|
|
|
// check for a password
|
|
if (!ent->bot_client)//ERASER
|
|
{
|
|
value = Info_ValueForKey (userinfo, "password");
|
|
if ((wfpassword) &&(strcmp(wfpassword->string, value) != 0))
|
|
return false;
|
|
}
|
|
|
|
// they can connect
|
|
ent->client = game.clients + (ent - g_edicts - 1);
|
|
|
|
// if there is already a body waiting for us (a loadgame), just
|
|
// take it, otherwise spawn one from scratch
|
|
if (ent->inuse == false)
|
|
{
|
|
// clear the respawning variables
|
|
//ZOID -- force team join
|
|
ent->client->resp.ctf_team = -1;
|
|
//ZOID
|
|
is_bot = ent->bot_client;//ERASER// make sure bot's join a team
|
|
InitClientResp (ent->client, ent);//WF34 ADDED ENT
|
|
is_bot = false;//ERASER
|
|
|
|
if (!game.autosaved || !ent->client->pers.weapon)
|
|
InitClientPersistant (ent->client);
|
|
if (!ent->wf_team) ent->wf_team = ent->client->resp.ctf_team;
|
|
}
|
|
//ERASER START
|
|
// do real client specific stuff
|
|
if (!ent->bot_client)
|
|
{
|
|
int i;
|
|
|
|
ent->client->team = NULL;
|
|
|
|
ent->client->lag_trail = gi.TagMalloc(sizeof(lag_trail_t), TAG_GAME);
|
|
ent->client->lag_angles = gi.TagMalloc(sizeof(lag_trail_t), TAG_GAME);
|
|
|
|
// init the lag_trail
|
|
for (i=0; i<10; i++)
|
|
{
|
|
VectorCopy(ent->s.origin, (*ent->client->lag_trail)[i]);
|
|
VectorCopy(ent->client->v_angle, (*ent->client->lag_angles)[i]);
|
|
}
|
|
}
|
|
//ERASER END
|
|
|
|
// Clear active grenade and special item counts
|
|
//gi.dprintf("Debug: clear grenade counts. After =\n");
|
|
for (i=1; i <= GRENADE_TYPE_COUNT; ++i)
|
|
ent->client->pers.active_grenades[i] = 0;
|
|
|
|
for (i=1; i <= SPECIAL_COUNT; ++i)
|
|
ent->client->pers.active_special[i] = 0;
|
|
|
|
ClientUserinfoChanged (ent, userinfo);
|
|
|
|
if (game.maxclients > 1)
|
|
gi.dprintf ("%s connected\n", ent->client->pers.netname);
|
|
|
|
//make sure they don't start with a old players class 5/99
|
|
if (!ent->bot_client)
|
|
{
|
|
if (ent->client->pers.player_class != 0)
|
|
{
|
|
ent->client->pers.player_class = 0;
|
|
ent->client->pers.next_player_class = 0;
|
|
// gi.dprintf("Connected with a class\n");
|
|
}
|
|
}
|
|
|
|
ent->svflags = 0; // make sure we start with known default
|
|
ent->client->pers.connected = true;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
ClientDisconnect
|
|
|
|
Called when a player drops from the server.
|
|
Will not be called between levels.
|
|
============
|
|
*/
|
|
void botRemovePlayer(edict_t *self);//ERASER
|
|
|
|
void ClientDisconnect (edict_t *ent)
|
|
{
|
|
int playernum;
|
|
//K2
|
|
int i;
|
|
edict_t *other;
|
|
//K2
|
|
|
|
if (!ent->client)
|
|
return;
|
|
|
|
//See if it's the ref
|
|
if (wf_game.ref_ent == ent)
|
|
wf_game.ref_ent = NULL;
|
|
|
|
//ERASER START
|
|
///Q2 Camera Begin
|
|
EntityListRemove(ent);
|
|
///Q2 Camera End
|
|
|
|
//K2: If player was in cammode, he was already removed 3/99
|
|
if(!ent->client->bIsCamera)
|
|
botRemovePlayer(ent);
|
|
|
|
//ERASER END
|
|
my_bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
|
|
|
|
// update chase cam if being followed
|
|
for (i = 0; i <= num_players; i++) {
|
|
|
|
other = players[i];
|
|
|
|
if(!other || other->bot_client)
|
|
continue;
|
|
|
|
if (other->inuse && other->client->chase_target == ent)
|
|
{
|
|
other->client->chase_target->inuse = false;
|
|
UpdateChaseCam(other);
|
|
}
|
|
}
|
|
//K2:End //3/99 botcam acrid
|
|
|
|
ent->client->resp.inServer = false;//3/99 botcam
|
|
|
|
|
|
//WF
|
|
sl_LogPlayerDisconnect( &gi, level, ent ); // StdLog - Mark Davies
|
|
WFPlayer_ChangeClassTeam(ent);
|
|
|
|
//WF
|
|
|
|
//ZOID
|
|
CTFDeadDropFlag(ent);
|
|
CTFDeadDropTech(ent);
|
|
//ZOID
|
|
|
|
// send effect
|
|
gi.WriteByte (svc_muzzleflash);
|
|
gi.WriteShort (ent-g_edicts);
|
|
gi.WriteByte (MZ_LOGOUT);
|
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
|
|
|
gi.unlinkentity (ent);
|
|
ent->s.modelindex = 0;
|
|
ent->solid = SOLID_NOT;
|
|
ent->inuse = false;
|
|
ent->classname = "disconnected";
|
|
ent->client->pers.connected = false;
|
|
|
|
playernum = ent-g_edicts-1;
|
|
gi.configstring (CS_PLAYERSKINS+playernum, "");
|
|
}
|
|
|
|
|
|
//==============================================================
|
|
|
|
|
|
edict_t *pm_passent;
|
|
|
|
// pmove doesn't need to know about passent and contentmask
|
|
trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
|
|
{
|
|
if (pm_passent->health > 0)
|
|
return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
|
|
else
|
|
return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
|
|
}
|
|
|
|
unsigned CheckBlock (void *b, int c)
|
|
{
|
|
int v,i;
|
|
v = 0;
|
|
for (i=0 ; i<c ; i++)
|
|
v+= ((byte *)b)[i];
|
|
return v;
|
|
}
|
|
void PrintPmove (pmove_t *pm)
|
|
{
|
|
unsigned c1, c2;
|
|
|
|
c1 = CheckBlock (&pm->s, sizeof(pm->s));
|
|
c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
|
|
Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ClientThink
|
|
|
|
This will be called once for each client frame, which will
|
|
usually be a couple times for each server frame.
|
|
==============
|
|
*/
|
|
int last_bot=0;//ERASER
|
|
#define BOT_THINK_TIME 0.03//ERASER// never do bot thinks for more than this time
|
|
|
|
void ClientThink (edict_t *ent, usercmd_t *ucmd)
|
|
{
|
|
gclient_t *client;
|
|
edict_t *other;
|
|
int i, j;
|
|
pmove_t pm;
|
|
qboolean jetting;
|
|
//ERASER START
|
|
int count=0, start;
|
|
clock_t start_time, now;
|
|
|
|
int playernum;
|
|
char userinfo[MAX_INFO_STRING];//acrid 3/99 freeze
|
|
|
|
//ZBOT DETECTION FROM LITHIUM
|
|
if ((int)wfflags->value & WF_ZBOT_DETECT)
|
|
{
|
|
if(ZbotCheck(ent, ucmd))
|
|
{
|
|
zbotLogAttack(ent, 1);
|
|
//my_bprintf (PRINT_MEDIUM,"SYSTEM MESSAGE: %s has been kicked for using a ZBot.\n", ent->client->pers.netname);
|
|
safe_cprintf(ent, PRINT_HIGH, "You have been kicked for using a ZBot.\n");
|
|
stuffcmd(ent, "disconnect\n"); // kick out zbots.
|
|
my_bprintf(PRINT_HIGH, "%s is using a ZBot!\n",ent->client->pers.netname);
|
|
I_AM_A_ZBOT(ent);
|
|
}
|
|
}
|
|
|
|
//ZBOT DETECTION FROM LITHIUM
|
|
|
|
if (paused)
|
|
{
|
|
gi.centerprintf(ent, "PAUSED\n\n(type \"botpause\" to resume)");
|
|
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
|
return;
|
|
}
|
|
|
|
//ERASER END
|
|
//Acrid freeze 3/99
|
|
if (ent->frozen)
|
|
{
|
|
if( level.time < ent->frozentime )
|
|
{ ent->client->ps.pmove.pm_type = PM_DEAD;
|
|
|
|
if (ent->client->buttons & BUTTON_ATTACK)//4/99 acrid
|
|
{
|
|
ent->client->buttons &= ~BUTTON_ATTACK;
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
playernum = ent - g_edicts - 1;
|
|
strcpy( userinfo, ent->client->pers.userinfo );
|
|
ent->frozen = 0;
|
|
Info_SetValueForKey( userinfo, "skin", ent->oldskin );
|
|
ClientUserinfoChanged( ent, userinfo );
|
|
}
|
|
}
|
|
|
|
level.current_entity = ent;
|
|
client = ent->client;
|
|
|
|
if (level.intermissiontime)
|
|
{
|
|
client->ps.pmove.pm_type = PM_FREEZE;
|
|
// can exit intermission after five seconds
|
|
if (level.time > level.intermissiontime + 5.0
|
|
&& (ucmd->buttons & BUTTON_ANY) )
|
|
level.exitintermission = true;
|
|
return;
|
|
}
|
|
|
|
|
|
//Feign Acrid
|
|
if(client->pers.feign)
|
|
{
|
|
if (ent->health <=0)
|
|
client->pers.feign = 0;
|
|
//oldfeign else
|
|
// return;
|
|
}
|
|
|
|
//ERASER START
|
|
///Q2 Camera Begin
|
|
if (ent->client->bIsCamera)
|
|
{
|
|
CameraThink(ent,ucmd);
|
|
return;
|
|
}
|
|
///Q2 Camera End
|
|
|
|
if (ent->bot_client)
|
|
return;
|
|
|
|
if (client->on_hook == true)//newgrap 4/99
|
|
{
|
|
CTFGrapplePull2(ent);
|
|
client->ps.pmove.gravity = 0;
|
|
}
|
|
else
|
|
{
|
|
client->ps.pmove.gravity = sv_gravity->value;
|
|
}
|
|
//gi.dprintf("f: %i, s: %i, u: %i\n", ucmd->forwardmove, ucmd->sidemove, ucmd->upmove);
|
|
//gi.dprintf("%i, %i\n", ucmd->buttons, client->ps.pmove.pm_flags);
|
|
//ERASER END 2 LINES ALREADY CO
|
|
pm_passent = ent;
|
|
|
|
//ZOID
|
|
if (ent->client->chase_target) {
|
|
client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
|
|
client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
|
|
client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
|
|
return;
|
|
}
|
|
//ZOID
|
|
|
|
// set up for pmove
|
|
memset (&pm, 0, sizeof(pm));
|
|
|
|
if (ent->movetype == MOVETYPE_NOCLIP)
|
|
client->ps.pmove.pm_type = PM_SPECTATOR;
|
|
else if (ent->s.modelindex != 255)
|
|
client->ps.pmove.pm_type = PM_GIB;
|
|
else if (ent->deadflag)
|
|
client->ps.pmove.pm_type = PM_DEAD;
|
|
else
|
|
client->ps.pmove.pm_type = PM_NORMAL;
|
|
|
|
//Enforce cl_forwardspeed and cl_sidespeed 5/99 acrid
|
|
if (!ent->bot_client)
|
|
{
|
|
if (ucmd->forwardmove > 400 || ucmd->sidemove > 400)
|
|
{
|
|
stuffcmd(ent,"cl_forwardspeed 200\n");
|
|
stuffcmd(ent,"cl_sidespeed 200\n");
|
|
}
|
|
}
|
|
|
|
// client->ps.pmove.gravity = sv_gravity->value;newgrap 4/99
|
|
//WF24 S
|
|
if (ent->flags & FL_BOOTS)
|
|
client->ps.pmove.gravity = sv_gravity->value * 0.25;
|
|
if(ent->Slower>level.time)
|
|
{
|
|
ucmd->forwardmove *= 0.5;
|
|
ucmd->sidemove *= 0.5;
|
|
ucmd->upmove *= 0.5;
|
|
}
|
|
//WF & ATTILA begin
|
|
//Turn off thrusting state if the WF_NO_FLYING flag is set
|
|
//This is set here so the feature can be turned off mid-game
|
|
//Some classes can't fly
|
|
if (ent->client &&
|
|
(((int)wfflags->value & WF_NO_FLYING) ||
|
|
((ent->client->player_special & SPECIAL_JETPACK) == 0)))
|
|
{
|
|
ent->client->thrusting = 0;
|
|
}
|
|
|
|
//Thrust if you are not all the way in the water
|
|
if ((ent->client->thrusting) && (ent->waterlevel < 3))
|
|
// if ( Jet_Active(ent) )
|
|
Jet_ApplyJet( ent, ucmd );//FIXME NODES
|
|
//WF24 E & ATTILA end
|
|
|
|
pm.s = client->ps.pmove;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pm.s.origin[i] = ent->s.origin[i]*8;
|
|
pm.s.velocity[i] = ent->velocity[i]*8;
|
|
}
|
|
//WF34 START
|
|
if(ent->superslow)
|
|
{
|
|
// ucmd->forwardmove *= 0.4;
|
|
// ucmd->sidemove *= 0.4;
|
|
// ucmd->upmove *= 0.4;
|
|
ucmd->forwardmove *= 0.75;
|
|
ucmd->sidemove *= 0.75;
|
|
ucmd->upmove *= 0.75;
|
|
}
|
|
if(ent->lame)
|
|
{
|
|
ucmd->forwardmove *= 0.5;
|
|
ucmd->sidemove *= 0.5;
|
|
ucmd->upmove *= 0.5;
|
|
}
|
|
//WF34 END
|
|
//Feign Acrid
|
|
if(client->pers.feign)//newfeign 3/99
|
|
{
|
|
ucmd->forwardmove *= 0.0;
|
|
ucmd->sidemove *= 0.0;
|
|
ucmd->upmove *= 0.0;
|
|
}
|
|
if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
|
|
{
|
|
pm.snapinitial = true;
|
|
// gi.dprintf ("pmove changed!\n");
|
|
}
|
|
|
|
pm.cmd = *ucmd;
|
|
|
|
pm.trace = PM_trace; // adds default parms
|
|
pm.pointcontents = gi.pointcontents;
|
|
|
|
// perform a pmove
|
|
gi.Pmove (&pm);
|
|
|
|
// save results of pmove
|
|
client->ps.pmove = pm.s;
|
|
client->old_pmove = pm.s;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
ent->s.origin[i] = pm.s.origin[i]*0.125;
|
|
ent->velocity[i] = pm.s.velocity[i]*0.125;
|
|
}
|
|
|
|
jetting = Jet_Active(ent);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
if ( !jetting
|
|
|| (jetting
|
|
&& (fabs((float)pm.s.velocity[i]*0.125) < fabs(ent->velocity[i]))) )
|
|
{
|
|
ent->velocity[i] = pm.s.velocity[i]*0.125;
|
|
}
|
|
}
|
|
|
|
//FIXME NODES? dont remember //newfeign 3/99
|
|
//Feign Acrid
|
|
if(!client->pers.feign)
|
|
{
|
|
VectorCopy (pm.mins, ent->mins);
|
|
VectorCopy (pm.maxs, ent->maxs);
|
|
}
|
|
client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
|
|
client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
|
|
client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
|
|
|
|
//WF24 S & ATTILA begin
|
|
if (jetting)
|
|
if( pm.groundentity ) //are we on ground
|
|
if ( Jet_AvoidGround(ent) ) //then lift us if possible
|
|
pm.groundentity = NULL; //FIXME NODES //now we are no longer on ground
|
|
//WF24 E & ATTILA end
|
|
|
|
|
|
if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
|
|
{
|
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
|
|
PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
|
|
}
|
|
|
|
//Feign Acrid
|
|
if(!client->pers.feign)//newfeign 3/99
|
|
ent->viewheight = pm.viewheight;
|
|
|
|
ent->waterlevel = pm.waterlevel;
|
|
ent->watertype = pm.watertype;
|
|
ent->groundentity = pm.groundentity;
|
|
if (pm.groundentity)
|
|
ent->groundentity_linkcount = pm.groundentity->linkcount;
|
|
|
|
if (ent->deadflag)
|
|
{
|
|
client->ps.viewangles[ROLL] = 40;
|
|
client->ps.viewangles[PITCH] = -15;
|
|
client->ps.viewangles[YAW] = client->killer_yaw;
|
|
}
|
|
//Feign Acrid
|
|
else if(!client->pers.feign)//newfeign 3/99
|
|
{
|
|
VectorCopy (pm.viewangles, client->v_angle);
|
|
VectorCopy (pm.viewangles, client->ps.viewangles);
|
|
}
|
|
|
|
|
|
//ZOID
|
|
if (ent->bot_client)//newgrap 4/99
|
|
{
|
|
if (client->ctf_grapple)
|
|
CTFGrapplePull(client->ctf_grapple);
|
|
}
|
|
//ZOID
|
|
|
|
|
|
// handle cloaking ability
|
|
if (ent->client->cloaking)
|
|
{
|
|
if (ucmd->forwardmove != 0 || ucmd->sidemove != 0)
|
|
{
|
|
ent->svflags &= ~SVF_NOCLIENT;
|
|
ent->client->cloaking = false;
|
|
}
|
|
else
|
|
{
|
|
if (ent->svflags & SVF_NOCLIENT)
|
|
{
|
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] >= CLOAK_AMMO)
|
|
{
|
|
ent->client->cloakdrain ++;
|
|
if (ent->client->cloakdrain == CLOAK_DRAIN)
|
|
{
|
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= CLOAK_AMMO;
|
|
ent->client->cloakdrain = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ent->svflags &= ~SVF_NOCLIENT;
|
|
ent->client->cloaking = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ent->client->cloaking)
|
|
{
|
|
if (level.time > ent->client->cloaktime)
|
|
{
|
|
ent->svflags |= SVF_NOCLIENT;
|
|
ent->client->cloakdrain = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ent->client->cloaktime = level.time + CLOAK_ACTIVATE_TIME;
|
|
ent->client->cloaking = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
gi.linkentity (ent);
|
|
|
|
if (ent->movetype != MOVETYPE_NOCLIP)
|
|
G_TouchTriggers (ent);
|
|
|
|
// touch other objects
|
|
for (i=0 ; i<pm.numtouch ; i++)
|
|
{
|
|
other = pm.touchents[i];
|
|
for (j=0 ; j<i ; j++)
|
|
if (pm.touchents[j] == other)
|
|
break;
|
|
if (j != i)
|
|
continue; // duplicated
|
|
if (!other->touch)
|
|
continue;
|
|
//acrid 3/99
|
|
if (other->frozen && other->client )
|
|
{
|
|
// only touch-unfreeze if we have the same skin acrid3
|
|
if( !strcmp( Info_ValueForKey( ent->client->pers.userinfo, "skin" ), other->oldskin))
|
|
{botDebugPrint("frozen and have same skin \n");
|
|
// find the player number
|
|
playernum = other - g_edicts - 1;
|
|
// get the userinfo
|
|
strcpy( userinfo, other->client->pers.userinfo );
|
|
// unfreeze me and restore skin
|
|
other->frozen = 0;
|
|
Info_SetValueForKey( userinfo, "skin", other->oldskin );
|
|
ClientUserinfoChanged( other, userinfo );
|
|
}
|
|
}
|
|
// ******************
|
|
|
|
other->touch (other, ent, NULL, NULL);
|
|
}
|
|
//ERASER START
|
|
if ((client->latency > 0) && !(client->buttons & BUTTON_ATTACK) && (ucmd->buttons & BUTTON_ATTACK))
|
|
{
|
|
if ((level.time - client->firing_delay) > 0.1)
|
|
client->firing_delay = level.time + (client->latency/1000);
|
|
|
|
if (client->firing_delay > level.time) // turn it off
|
|
{
|
|
ucmd->buttons &= ~BUTTON_ATTACK;
|
|
if (ucmd->buttons >= BUTTON_ANY)
|
|
ucmd->buttons -= BUTTON_ANY;
|
|
}
|
|
}
|
|
|
|
if ((client->latency > 0) && !(ucmd->buttons & BUTTON_ATTACK) && (client->firing_delay > (level.time - 0.1)) && (client->firing_delay <= level.time))
|
|
{ // remember the button after it was released when simulating lag
|
|
ucmd->buttons |= BUTTON_ATTACK;
|
|
}
|
|
//ERASER END
|
|
client->oldbuttons = client->buttons;
|
|
client->buttons = ucmd->buttons;
|
|
client->latched_buttons |= client->buttons & ~client->oldbuttons;
|
|
|
|
// save light level the player is standing on for
|
|
// monster sighting AI
|
|
ent->light_level = ucmd->lightlevel;
|
|
|
|
// fire weapon from final position if needed
|
|
if (client->latched_buttons & BUTTON_ATTACK
|
|
//ZOID
|
|
&& ent->movetype != MOVETYPE_NOCLIP
|
|
//ZOID
|
|
)
|
|
{
|
|
if (!client->weapon_thunk)
|
|
{
|
|
client->weapon_thunk = true;
|
|
Think_Weapon (ent);
|
|
}
|
|
}
|
|
//WF24 S ST
|
|
if ((ent->client->kamikaze_mode & 1) && (ent->client->kamikaze_framenum <= level.framenum))
|
|
Kamikaze_Explode(ent);
|
|
//WF24 E ST
|
|
|
|
//ZOID
|
|
//regen tech
|
|
CTFApplyRegeneration(ent);
|
|
//ZOID
|
|
|
|
//ZOID
|
|
//K2: Changed to use playerlist botcam
|
|
// update chase cam if being followed
|
|
// for (i = 1; i <= maxclients->value; i++) {
|
|
// other = g_edicts + i;
|
|
for (i = 0; i <= num_players; i++) {
|
|
|
|
other = players[i];
|
|
|
|
if (!other || other->bot_client)
|
|
continue;
|
|
if (other->inuse && other->client->chase_target == ent)//orig from here
|
|
UpdateChaseCam(other);
|
|
}
|
|
//ZOID
|
|
//WF34 START
|
|
//WF
|
|
if(ent->cantmove)
|
|
VectorCopy(ent->LockedPosition,ent->s.origin);
|
|
//WF34 END
|
|
|
|
//ERASER START
|
|
// =========================================================
|
|
// check for a bots to think
|
|
if ((num_players > 2) && (num_clients == 1))
|
|
{
|
|
int save=-1, loop_count=0;
|
|
vec3_t org;
|
|
|
|
start = last_bot;
|
|
start_time = clock();
|
|
now = start_time;
|
|
|
|
// go to the next bot
|
|
last_bot++;
|
|
if (last_bot >= num_players)
|
|
last_bot = 0;
|
|
|
|
VectorCopy(ent->s.origin, org);
|
|
org[2] += ent->viewheight;
|
|
|
|
while ( (count < 5) && (((double) (now - start_time)) < 50))
|
|
{
|
|
// don't do client's, or bot's in this client's view
|
|
while ( (players[last_bot]->last_think_time == level.time)
|
|
|| (!players[last_bot]->bot_client)
|
|
|| ( (gi.inPVS(org, players[last_bot]->s.origin))
|
|
&& ( (players[last_bot]->nextthink >= level.time)
|
|
|| (players[last_bot]->nextthink = level.time + 0.1)
|
|
)
|
|
) // this makes the bot think by itself (smooth movement)
|
|
)
|
|
{
|
|
last_bot++;
|
|
if (last_bot == num_players)
|
|
last_bot = 0;
|
|
loop_count++;
|
|
|
|
if (players[last_bot]->bot_client && (last_bot == start)) // we've done all bots
|
|
goto done_all_bots;
|
|
|
|
if (loop_count > num_players)
|
|
goto done_all_bots;
|
|
}
|
|
|
|
if (start == 0)
|
|
start = last_bot;
|
|
|
|
if ((level.time - players[last_bot]->last_think_time) >= 0.1)
|
|
{
|
|
players[last_bot]->think(players[last_bot]);
|
|
players[last_bot]->nextthink = -1; // don't think by themselves
|
|
count++;
|
|
|
|
save = last_bot;
|
|
}
|
|
|
|
if (last_bot == start) // we've done all bots
|
|
break;
|
|
|
|
last_bot++;
|
|
if (last_bot == num_players)
|
|
last_bot = 0;
|
|
loop_count++;
|
|
if (loop_count > num_players)
|
|
goto done_all_bots;
|
|
|
|
now = clock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done_all_bots:
|
|
/*
|
|
// check for firing between server frames
|
|
for (i=0; i<num_players; i++)
|
|
{
|
|
if ( (players[i]->bot_client)
|
|
&& (players[i]->dmg)
|
|
// && ( (timebuffer.millitm < players[i]->lastattack_time.millitm)
|
|
// || ((timebuffer.millitm - players[i]->lastattack_time.millitm) >= 50)
|
|
// )
|
|
)
|
|
{
|
|
bot_FireWeapon(players[i]);
|
|
players[i]->dmg = 0;
|
|
}
|
|
}
|
|
*/
|
|
if (!dedicated->value)
|
|
OptimizeRouteCache();//crash???
|
|
|
|
// =========================================================
|
|
// Check to see if player pressing the "use" key newgrap 4/99
|
|
if (ent->client->buttons & BUTTON_USE && !ent->deadflag &&
|
|
client->hook_frame <= level.framenum)
|
|
{
|
|
if((ent->client->player_special & SPECIAL_GRAPPLE) == 0)
|
|
{
|
|
safe_cprintf(ent, PRINT_HIGH, "Sorry - This class cannot use the grapple.\n");
|
|
return;
|
|
}
|
|
else if (ent->movetype == MOVETYPE_NOCLIP)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
int damage = 10;
|
|
CTFGrappleFire2 (ent, vec3_origin, damage, 0);
|
|
// CTFWeapon_Grapple_Fire2 (ent);
|
|
}
|
|
}
|
|
if (Ended_Grappling (client) && !ent->deadflag && client->ctf_grapple)
|
|
{
|
|
CTFPlayerResetGrapple2(ent);
|
|
}
|
|
|
|
}
|
|
|
|
//ERASER END
|
|
|
|
/*
|
|
==============
|
|
ClientBeginServerFrame
|
|
|
|
This will be called once for each server frame, before running
|
|
any other entities in the world.
|
|
==============
|
|
*/
|
|
//ERASER START
|
|
void trigger_elevator_use (edict_t *self, edict_t *other, edict_t *activator);
|
|
void Use_Plat (edict_t *ent, edict_t *other, edict_t *activator);
|
|
void CTFFlagThink(edict_t *ent);
|
|
//ERASER END
|
|
void VectorRotate(vec3_t in, vec3_t angles, vec3_t out); //WF34
|
|
|
|
|
|
void ClientBeginServerFrame (edict_t *ent)
|
|
{
|
|
gclient_t *client;
|
|
int i;//ERASER
|
|
int buttonMask;
|
|
// qboolean noise = false;
|
|
// vec3_t temp,new;//WF34
|
|
// float volume = 1.0;
|
|
// int blood;//WF34
|
|
|
|
if (ent->bot_client)//ERASER
|
|
return;//E
|
|
|
|
if (level.intermissiontime)
|
|
return;
|
|
//ERASER START
|
|
///Q2 Camera Begin
|
|
if (ent->client->bIsCamera)
|
|
{
|
|
return;
|
|
}
|
|
///Q2 Camera End
|
|
//ERASER END
|
|
client = ent->client;
|
|
|
|
/* C.O. For Lithium Test
|
|
// -- ANTI-ZBOT STARTS -- //
|
|
if (ent->client->resp.bot_start > 0 && ent->client->resp.bot_start <= level.time)
|
|
{
|
|
//ent->client->resp.bot_start = 0;
|
|
//stuffcmd(ent, "!zbot\n");
|
|
//ent->client->resp.bot_end = level.time + 4;
|
|
//stuffcmd(ent, "@zbot\n");
|
|
//ent->client->resp.bot_end2 = level.time + 4;
|
|
//stuffcmd(ent, "#zbot\n");
|
|
//ent->client->resp.bot_end3 = level.time + 4;
|
|
}
|
|
|
|
if (ent->client->resp.bot_end > 0 && ent->client->resp.bot_end <= level.time)
|
|
{
|
|
//If the player ping is too high, try again later
|
|
if (ent->client->resp.bot_retries <= 4)
|
|
{
|
|
ent->client->resp.bot_start = level.time + 5 + (rand() % 5);
|
|
ent->client->resp.bot_end = 0;
|
|
++ent->client->resp.bot_retries;
|
|
}
|
|
else
|
|
{
|
|
ent->client->resp.bot_end = 0;
|
|
zbotLogAttack(ent, 1);
|
|
//my_bprintf (PRINT_MEDIUM,"SYSTEM MESSAGE: %s has been kicked for using a ZBot.\n", ent->client->pers.netname);
|
|
safe_cprintf(ent, PRINT_HIGH, "You have been kicked for using a ZBot (or you have a bad connection).\n");
|
|
stuffcmd(ent, "disconnect\n"); // kick out zbots.
|
|
}
|
|
}
|
|
|
|
if (ent->client->resp.bot_end2 > 0 && ent->client->resp.bot_end2 <= level.time)
|
|
{
|
|
//If the player ping is too high, try again later
|
|
if (ent->client->resp.bot_retries2 <= 4)
|
|
{
|
|
ent->client->resp.bot_start = level.time + 5 + (rand() % 5);
|
|
ent->client->resp.bot_end2 = 0;
|
|
++ent->client->resp.bot_retries2;
|
|
}
|
|
else
|
|
{
|
|
ent->client->resp.bot_end2 = 0;
|
|
zbotLogAttack(ent, 2);
|
|
//my_bprintf (PRINT_MEDIUM,"SYSTEM MESSAGE: %s has been kicked for using a ZBot.\n", ent->client->pers.netname);
|
|
safe_cprintf(ent, PRINT_HIGH, "You have been kicked for using a ZBot (or you have a bad connection).\n");
|
|
stuffcmd(ent, "disconnect\n"); // kick out zbots.
|
|
}
|
|
}
|
|
|
|
if (ent->client->resp.bot_end3 > 0 && ent->client->resp.bot_end3 <= level.time)
|
|
{
|
|
//If the player ping is too high, try again later
|
|
if (ent->client->resp.bot_retries3 <= 4)
|
|
{
|
|
ent->client->resp.bot_start = level.time + 5 + (rand() % 5);
|
|
ent->client->resp.bot_end3 = 0;
|
|
++ent->client->resp.bot_retries3;
|
|
}
|
|
else
|
|
{
|
|
ent->client->resp.bot_end3 = 0;
|
|
zbotLogAttack(ent, 3);
|
|
//my_bprintf (PRINT_MEDIUM,"SYSTEM MESSAGE: %s has been kicked for using a ZBot.\n", ent->client->pers.netname);
|
|
safe_cprintf(ent, PRINT_HIGH, "You have been kicked for using a ZBot (or you have a bad connection).\n");
|
|
stuffcmd(ent, "disconnect\n"); // kick out zbots.
|
|
}
|
|
}
|
|
|
|
|
|
C.O. For Lithium Test
|
|
|
|
*/
|
|
// -- ANTI-ZBOT ENDS -- //
|
|
|
|
// run weapon animations if it hasn't been done by a ucmd_t
|
|
if (!client->weapon_thunk
|
|
//ZOID
|
|
&& ent->movetype != MOVETYPE_NOCLIP
|
|
//ZOID
|
|
)
|
|
Think_Weapon (ent);
|
|
else
|
|
client->weapon_thunk = false;
|
|
|
|
//ERASER START
|
|
if (client->latency > 0)
|
|
{
|
|
// -- save the location in the lag_trail
|
|
// filter the trails down one position
|
|
for (i=9; i>0; i--)
|
|
{
|
|
VectorCopy((*client->lag_trail)[i-1], (*client->lag_trail)[i]);
|
|
VectorCopy((*client->lag_angles)[i-1], (*client->lag_angles)[i]);
|
|
}
|
|
|
|
VectorCopy(ent->s.origin, (*client->lag_trail)[0]);
|
|
VectorCopy(ent->client->v_angle, (*client->lag_angles)[0]);
|
|
// -- done.
|
|
}
|
|
//ERASER END
|
|
|
|
if (ent->deadflag)
|
|
{
|
|
// wait for any button just going down
|
|
if ( level.time > client->respawn_time)
|
|
{
|
|
// in deathmatch, only wait for attack button
|
|
if (deathmatch->value)
|
|
buttonMask = BUTTON_ATTACK;
|
|
else
|
|
buttonMask = -1;
|
|
|
|
if ( ( client->latched_buttons & buttonMask ) ||
|
|
(deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
|
|
{
|
|
respawn(ent);
|
|
client->latched_buttons = 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// add player trail so monsters can follow
|
|
|
|
/* if (!deathmatch->value)
|
|
// if ((int)wfflags->value & WF_DECOY_PURSUE)
|
|
|
|
if (!visible (ent, PlayerTrail_LastSpot() ) )
|
|
// PlayerTrail_Add (ent->s.old_origin);//WF24 LINE//ACRID FIXME MAJOR TROUBLE
|
|
PlayerTrail_Add (ent, ent->s.old_origin, NULL, false, false, NODE_NORMAL);
|
|
*///FIXME NODES
|
|
//WF24 E
|
|
client->latched_buttons = 0;
|
|
//WF34 START
|
|
|
|
if ((ent->disguised ||ent->disguisedteam) && (ent->disguiseshots==0))
|
|
{
|
|
WFRemoveDisguise(ent);
|
|
}
|
|
|
|
//See if menu is up
|
|
if (ent->client->menu)
|
|
{
|
|
//check timeout value of menu
|
|
if ((client->menu->MenuTimeout) && (level.time > client->menu->MenuTimeout))
|
|
{
|
|
client->menu->MenuTimeout = 0;
|
|
PMenu_Close(ent);
|
|
}
|
|
}
|
|
|
|
|
|
//ERASER START
|
|
// if (gi.pointcontents(ent->s.origin) & CONTENTS_LADDER)
|
|
|
|
// check for new node(s)
|
|
if (bot_calc_nodes->value)
|
|
{
|
|
CheckMoveForNodes(ent);
|
|
}
|
|
|
|
//if (ent->groundentity)
|
|
//gi.dprintf("%s\n", ent->groundentity->classname);
|
|
|
|
if (ent->flags & FL_SHOWPATH)
|
|
{
|
|
// set the target location
|
|
if (!ent->goalentity) // spawn it
|
|
{
|
|
ent->goalentity = G_Spawn();
|
|
ent->goalentity->classname = "player goal";
|
|
VectorCopy(ent->mins, ent->goalentity->mins);
|
|
VectorCopy(ent->maxs, ent->goalentity->maxs);
|
|
|
|
VectorCopy(ent->s.origin, ent->goalentity->s.origin);
|
|
ent->goalentity->s.modelindex = 255;
|
|
gi.linkentity(ent->goalentity);
|
|
}
|
|
|
|
if (ent->client->buttons & BUTTON_ATTACK)
|
|
{
|
|
VectorCopy(ent->s.origin, ent->goalentity->s.origin);
|
|
ent->goalentity->s.modelindex = 255;
|
|
gi.linkentity(ent->goalentity);
|
|
}
|
|
|
|
Debug_ShowPathToGoal(ent, ent->goalentity);
|
|
}
|
|
|
|
//gi.dprintf("%i\n", ent->waterlevel);
|
|
|
|
// HACK, send bots to us if we have the flag, and also summon some helper bots
|
|
if (ctf->value && CarryingFlag(ent))// &&
|
|
// (!ent->movetarget || !ent->movetarget->item || (ent->movetarget->item->pickup != CTFPickup_Flag)))
|
|
{
|
|
edict_t *flag, *enemy_flag, *plyr, *self;
|
|
int i=0, count=0, ideal;
|
|
static float last_checkhelp=0;
|
|
|
|
self = ent;
|
|
|
|
if (self->client->resp.ctf_team == CTF_TEAM1)
|
|
{
|
|
flag = flag1_ent;
|
|
enemy_flag = flag2_ent;
|
|
}
|
|
else
|
|
{
|
|
flag = flag2_ent;
|
|
enemy_flag = flag1_ent;
|
|
}
|
|
|
|
// look for some helpers
|
|
if (last_checkhelp < (level.time - 0.5))
|
|
{
|
|
for (i=0; i<num_players; i++)
|
|
{
|
|
plyr = players[i];
|
|
|
|
if (plyr->client->resp.ctf_team != self->client->resp.ctf_team)
|
|
{
|
|
if ( (plyr->enemy != self)
|
|
&& (!plyr->target_ent ||
|
|
(plyr->target_ent->think != CTFFlagThink) ||
|
|
(entdist(plyr, plyr->target_ent) > 1000))
|
|
&& (entdist(plyr, self) < 2000))
|
|
{ // send this enemy to us
|
|
plyr->enemy = self;
|
|
}
|
|
// continue;
|
|
}
|
|
|
|
if ((plyr != self) && (plyr->target_ent == self))
|
|
count++;
|
|
}
|
|
|
|
ideal = ((int)ceil((1.0*(float)num_players)/4.0));
|
|
|
|
if (count < ideal)
|
|
{
|
|
for (i=0; (i<num_players && count<ideal); i++)
|
|
{
|
|
plyr = players[i];
|
|
|
|
if (plyr->client->resp.ctf_team != self->client->resp.ctf_team)
|
|
continue;
|
|
|
|
if (plyr->target_ent == self)
|
|
continue;
|
|
|
|
if (entdist(plyr, self) > 700)
|
|
continue;
|
|
|
|
if (!gi.inPVS(plyr->s.origin, self->s.origin))
|
|
continue;
|
|
|
|
plyr->target_ent = self;
|
|
if (++count >= ideal)
|
|
break;
|
|
}
|
|
}
|
|
else if (count > ideal) // release a defender
|
|
{
|
|
for (i=0; (i<num_players && count<ideal); i++)
|
|
{
|
|
plyr = players[i];
|
|
|
|
if (plyr->client->resp.ctf_team != self->client->resp.ctf_team)
|
|
continue;
|
|
|
|
if (plyr->target_ent != self)
|
|
continue;
|
|
|
|
plyr->target_ent = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
last_checkhelp = level.time + random()*0.5;
|
|
}
|
|
|
|
}
|
|
|
|
if (ent->flags & FL_SHOW_FLAGPATHS)
|
|
{
|
|
edict_t *trav;
|
|
|
|
// show lines between alternate routes
|
|
trav = NULL;
|
|
while (trav = G_Find(trav, FOFS(classname), "flag_path_src"))
|
|
{
|
|
if (!trav->last_goal || !trav->target_ent)
|
|
continue; // not complete, don't save
|
|
|
|
trav->s.modelindex = gi.modelindex ("models/objects/gibs/chest/tris.md2");
|
|
|
|
if (trav->last_goal)
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_BFG_LASER);
|
|
gi.WritePosition (trav->s.origin);
|
|
gi.WritePosition (trav->last_goal->s.origin);
|
|
gi.multicast (trav->s.origin, MULTICAST_PVS);
|
|
}
|
|
if (trav->target_ent)
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_BFG_LASER);
|
|
gi.WritePosition (trav->s.origin);
|
|
gi.WritePosition (trav->target_ent->s.origin);
|
|
gi.multicast (trav->s.origin, MULTICAST_PHS);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
//ERASER END
|