tfc/src/shared/player.qc

920 lines
20 KiB
Plaintext

/*
* Copyright (c) 2016-2021 Marco Cawthorne <marco@icculus.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef CLIENT
/* Here's a list of bone names that we are aware of on HL player models.
Usually we'd use skeletalobjects to share the same skeleton/anim with
another model - but because FTEQW does not support that for HLMDL we
are forced to manually position the bones of our attachnment
by iterating over them and manually setting their position in 3D-space.
*/
string g_pbones[] =
{
"Bip01",
"Bip01 Footsteps",
"Bip01 Pelvis",
"Bip01 L Leg",
"Bip01 L Leg1",
"Bip01 L Foot",
"Bip01 L Toe0",
"Bip01 L Toe01",
"Bip01 L Toe02",
"Dummy16",
"Bip01 R Leg",
"Bip01 R Leg1",
"Bip01 R Foot",
"Bip01 R Toe0",
"Bip01 R Toe01",
"Bip01 R Toe02",
"Dummy11",
"Bip01 Spine",
"Bip01 Spine1",
"Bip01 Spine2",
"Bip01 Spine3",
"Bip01 Neck",
"Bip01 Head",
"Dummy21",
"Dummy08",
"Bone02",
"Bone03",
"Bone04",
"Dummy05",
"Bone09",
"Bone10",
"Dummy04",
"Bone05",
"Bone06",
"Dummy03",
"Bone07",
"Bone08",
"Dummy09",
"Bone11",
"Bone12",
"Dummy10",
"Bone13",
"Bone14",
"Bone15",
"Bip01 L Arm",
"Bip01 L Arm1",
"Bip01 L Arm2",
"Bip01 L Hand",
"Bip01 L Finger0",
"Bip01 L Finger01",
"Bip01 L Finger02",
"Dummy06",
"Bip01 L Finger1",
"Bip01 L Finger11",
"Bip01 L Finger12",
"Dummy07",
"Bip01 R Arm",
"Bip01 R Arm1",
"Bip01 R Arm2",
"Bip01 R Hand",
"Bip01 R Finger0",
"Bip01 R Finger01",
"Bip01 R Finger02",
"Dummy01",
"Bip01 R Finger1",
"Bip01 R Finger11",
"Bip01 R Finger12",
"Dummy02",
"Box02",
"Bone08",
"Bone15"
};
#endif
/* all potential SendFlags bits we can possibly send */
enumflags
{
PLAYER_TOPFRAME = PLAYER_CUSTOMFIELDSTART,
PLAYER_BOTTOMFRAME,
PLAYER_AMMO1,
PLAYER_AMMO2,
PLAYER_AMMO3,
PLAYER_UNUSED5,
PLAYER_UNUSED6,
PLAYER_UNUSED7
};
class player:NSClientPlayer
{
/* class info */
PREDICTED_INT(classtype)
/* animation */
PREDICTED_INT(anim_top)
PREDICTED_FLOAT(anim_top_time)
PREDICTED_FLOAT(anim_top_delay)
PREDICTED_INT(anim_bottom)
PREDICTED_FLOAT(anim_bottom_time)
/* ammo 1 */
PREDICTED_INT(mag_sbs)
PREDICTED_INT(mag_dbs)
PREDICTED_INT(mag_rpg)
PREDICTED_INT(mag_glauncher)
/* ammo 2 */
PREDICTED_INT(m_iAmmoRockets)
PREDICTED_INT(m_iAmmoNails)
PREDICTED_INT(m_iAmmoCells)
PREDICTED_INT(m_iAmmoShells)
PREDICTED_INT(m_iAmmoDetpack)
PREDICTED_INT(m_iAmmoMedikit)
/* ammo 3 */
PREDICTED_INT(mode_tempstate)
virtual void Physics_Jump(void);
virtual float Physics_MaxSpeed(void);
virtual void ProcessInput(void);
nonvirtual void TFC_CookGren1(void);
nonvirtual void TFC_CookGren2(void);
nonvirtual void TFC_ReleaseGren1(void);
nonvirtual void TFC_ReleaseGren2(void);
virtual void UpdatePlayerAnimation(float);
#ifdef CLIENT
virtual void ReceiveEntity(float,float);
virtual void PredictPreFrame(void);
virtual void PredictPostFrame(void);
virtual void UpdateAliveCam(void);
virtual void UpdatePlayerAttachments(bool);
#else
NSTimer gren1;
NSTimer gren2;
int m_iMaxHealth;
int m_iMaxArmor;
int m_iMaxShells;
int m_iMaxNails;
int m_iMaxRockets;
int m_iMaxCells;
int m_iMaxDetpack;
int m_iMaxMedikit;
virtual void EvaluateEntity(void);
virtual float SendEntity(entity, float);
virtual void SpawnIntoGame(void);
virtual void MakeClass(classtype_e);
virtual void ServerInputFrame(void);
nonvirtual void TFC_FragSelf(void);
nonvirtual void TFC_FragSelf(void);
#endif
};
void Animation_PlayerUpdate(player);
void Animation_TimerUpdate(player, float);
void
player::UpdatePlayerAnimation(float timelength)
{
/* calculate our skeletal progression */
Animation_PlayerUpdate(this);
/* advance animation timers */
Animation_TimerUpdate(this, timelength);
}
#ifdef SERVER
void TFCNade_ThrowCaltrop(player);
void TFCNade_ThrowHandGrenade(player);
void TFCNade_ThrowSecondary(player);
void TFCNade_SelfExplode(player);
void
player::TFC_FragSelf(void)
{
print("Primary exploded in your hand!\n");
TFCNade_SelfExplode(this);
}
void
player::TFC_ThrowSecondary(void)
{
print("Secondary exploded in your hand!\n");
TFCNade_ThrowSecondary(this);
}
#endif
void
player::TFC_CookGren1(void)
{
/* we're already cooking it */
if (gflags & GF_GREN1COOK)
return;
if (classtype == CLASS_SCOUT) {
/* caltrop sound */
#ifdef SERVER
StartSound("weapons/tink1.wav", CHAN_AUTO, 0, true);
#endif
} else {
/* grenade timer sound */
#ifdef SERVER
StartSound("weapons/timer.wav", CHAN_AUTO, 0, true);
gren1 = gren1.SetupTimer(this, TFC_FragSelf, 3.75f, false);
gren1.RunTimer();
#endif
}
gflags |= GF_GREN1COOK;
}
void
player::TFC_ReleaseGren1(void)
{
if (!(gflags & GF_GREN1COOK))
return;
if (classtype == CLASS_SCOUT) {
/* release caltrop */
#ifdef SERVER
TFCNade_ThrowCaltrop(this);
#endif
} else {
/* release the nade! */
#ifdef SERVER
TFCNade_ThrowHandGrenade(this);
gren1.StopTimer();
#endif
}
gflags &= ~GF_GREN1COOK;
}
void
player::TFC_CookGren2(void)
{
if (gflags & GF_GREN2COOK)
return;
if (classtype == CLASS_SNIPER)
return;
#ifdef SERVER
StartSound("weapons/timer.wav", CHAN_AUTO, 0, true);
gren2 = gren2.SetupTimer(this, TFC_ThrowSecondary, 3.75f, false);
gren2.RunTimer();
#endif
gflags |= GF_GREN2COOK;
}
void
player::TFC_ReleaseGren2(void)
{
if (!(gflags & GF_GREN2COOK))
return;
#ifdef SERVER
TFCNade_ThrowSecondary(this);
gren2.StopTimer();
#endif
gflags &= ~GF_GREN2COOK;
}
void
player::ProcessInput(void)
{
super::ProcessInput();
if (input_buttons & INPUT_BUTTON6)
TFC_CookGren1();
else
TFC_ReleaseGren1();
if (input_buttons & INPUT_BUTTON7)
TFC_CookGren2();
else
TFC_ReleaseGren2();
}
#ifdef CLIENT
.string oldmodel;
string Weapons_GetPlayermodel(player, int);
void
player::UpdatePlayerAttachments(bool visible)
{
/* draw the flashlight */
if (gflags & GF_FLASHLIGHT) {
vector src;
vector ang;
if (entnum != player_localentnum) {
src = origin + view_ofs;
ang = v_angle;
} else {
src = pSeat->m_vecPredictedOrigin + [0,0,-8];
ang = view_angles;
}
makevectors(ang);
traceline(src, src + (v_forward * 8096), MOVE_NORMAL, this);
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
dynamiclight_add(trace_endpos + (v_forward * -2), 128, [1,1,1]);
} else {
float p = dynamiclight_add(src, 512, [1,1,1], 0, "textures/flashlight");
dynamiclight_set(p, LFIELD_ANGLES, ang);
dynamiclight_set(p, LFIELD_FLAGS, 3);
}
}
/* FIXME: this needs to be incorporated and simplified, now that we can handle it all in-class */
if (!visible)
return;
/* what's the current weapon model supposed to be anyway? */
p_model.oldmodel = Weapons_GetPlayermodel(this, activeweapon);
/* we changed weapons, update skeletonindex */
if (p_model.model != p_model.oldmodel) {
/* free memory */
if (p_model.skeletonindex)
skel_delete(p_model.skeletonindex);
/* set the new model and mark us updated */
setmodel(p_model, p_model.oldmodel);
p_model.model = p_model.oldmodel;
/* set the new skeletonindex */
p_model.skeletonindex = skel_create(p_model.modelindex);
/* hack this thing in here FIXME: this should be done when popping in/out of a pvs */
if (autocvar(cl_himodels, 1, "Use high-quality thisayer models over lower-definition ones"))
setcustomskin(this, "", "geomset 0 2\n");
else
setcustomskin(this, "", "geomset 0 1\n");
}
/* follow thisayer at all times */
setorigin(p_model, origin);
p_model.angles = angles;
skel_build(p_model.skeletonindex, p_model, p_model.modelindex,0, 0, -1);
/* we have to loop through all valid bones of the weapon model and match them
* to the thisayer one */
for (float i = 0; i < g_pbones.length; i++) {
vector bpos;
float pbone = gettagindex(this, g_pbones[i]);
float wbone = gettagindex(p_model, g_pbones[i]);
/* if the bone doesn't ignore in either skeletal mesh, ignore */
if (wbone <= 0 || pbone <= 0)
continue;
bpos = gettaginfo(this, pbone);
/* the most expensive bit */
skel_set_bone_world(p_model, wbone, bpos, v_forward, v_right, v_up);
}
}
void Weapons_AmmoUpdate(entity);
void HUD_AmmoNotify_Check(player pl);
void HUD_ItemNotify_Check(player pl);
void Camera_RunPosBob(vector angles, __inout vector camera_pos);
void Camera_StrafeRoll(__inout vector camera_angle);
void Shake_Update(NSClientPlayer);
void
player::UpdateAliveCam(void)
{
vector cam_pos = GetEyePos();
Camera_RunPosBob(view_angles, cam_pos);
g_view.SetCameraOrigin(cam_pos);
Camera_StrafeRoll(view_angles);
g_view.SetCameraAngle(view_angles);
if (vehicle) {
NSVehicle veh = (NSVehicle)vehicle;
if (veh.UpdateView)
veh.UpdateView();
} else if (health) {
if (autocvar_pm_thirdPerson == TRUE) {
makevectors(view_angles);
vector vStart = [pSeat->m_vecPredictedOrigin[0], pSeat->m_vecPredictedOrigin[1], pSeat->m_vecPredictedOrigin[2] + 16] + (v_right * 4);
vector vEnd = vStart + (v_forward * -48) + [0,0,16] + (v_right * 4);
traceline(vStart, vEnd, FALSE, this);
g_view.SetCameraOrigin(trace_endpos + (v_forward * 5));
}
}
Shake_Update(this);
g_view.AddPunchAngle(punchangle);
}
/*
=================
player::ReceiveEntity
=================
*/
void
player::ReceiveEntity(float new, float fl)
{
/* the generic client attributes */
NSClientPlayer::ReceiveEntity(new, fl);
/* animation */
if (fl & PLAYER_TOPFRAME) {
anim_top = readbyte();
anim_top_time = readfloat();
anim_top_delay = readfloat();
}
if (fl & PLAYER_BOTTOMFRAME) {
anim_bottom = readbyte();
anim_bottom_time = readfloat();
}
if (fl & PLAYER_AMMO1) {
mag_sbs = readbyte();
mag_dbs = readbyte();
mag_rpg = readbyte();
mag_glauncher = readbyte();
}
if (fl & PLAYER_AMMO2) {
m_iAmmoRockets = readbyte();
m_iAmmoNails = readbyte();
m_iAmmoCells = readbyte();
m_iAmmoShells = readbyte();
m_iAmmoDetpack = readbyte();
m_iAmmoMedikit = readbyte();
}
if (fl & PLAYER_AMMO3) {
mode_tempstate = readbyte();
classtype = readbyte();
}
setorigin(this, origin);
/* these only concern the current player */
CSQC_UpdateSeat();
if (this != pSeat->m_ePlayer)
return;
/* do not notify us of updates when spawning initially */
if (fl == UPDATE_ALL)
PredictPreFrame();
if (fl & PLAYER_AMMO1 || fl & PLAYER_AMMO2 || fl & PLAYER_AMMO3) {
Weapons_AmmoUpdate(this);
HUD_AmmoNotify_Check(this);
}
if (fl & PLAYER_ITEMS || fl & PLAYER_HEALTH)
HUD_ItemNotify_Check(this);
}
/*
=================
player::PredictPostFrame
Save the last valid server values away in the _net variants of each field
so we can roll them back later.
=================
*/
void
player::PredictPreFrame(void)
{
/* the generic client attributes */
NSClientPlayer::PredictPreFrame();
SAVE_STATE(anim_top);
SAVE_STATE(anim_top_delay);
SAVE_STATE(anim_top_time);
SAVE_STATE(anim_bottom);
SAVE_STATE(anim_bottom_time);
SAVE_STATE(mag_sbs);
SAVE_STATE(mag_dbs);
SAVE_STATE(mag_rpg);
SAVE_STATE(mag_glauncher);
SAVE_STATE(m_iAmmoRockets);
SAVE_STATE(m_iAmmoNails);
SAVE_STATE(m_iAmmoCells);
SAVE_STATE(m_iAmmoShells);
SAVE_STATE(m_iAmmoDetpack);
SAVE_STATE(m_iAmmoMedikit);
SAVE_STATE(mode_tempstate);
SAVE_STATE(classtype);
}
/*
=================
player::PredictPostFrame
Where we roll back our values to the ones last sent/verified by the server.
=================
*/
void
player::PredictPostFrame(void)
{
/* the generic client attributes */
NSClientPlayer::PredictPostFrame();
ROLL_BACK(anim_top);
ROLL_BACK(anim_top_delay);
ROLL_BACK(anim_top_time);
ROLL_BACK(anim_bottom);
ROLL_BACK(anim_bottom_time);
ROLL_BACK(mag_sbs);
ROLL_BACK(mag_dbs);
ROLL_BACK(mag_rpg);
ROLL_BACK(mag_glauncher);
ROLL_BACK(m_iAmmoRockets);
ROLL_BACK(m_iAmmoNails);
ROLL_BACK(m_iAmmoCells);
ROLL_BACK(m_iAmmoShells);
ROLL_BACK(m_iAmmoDetpack);
ROLL_BACK(m_iAmmoMedikit);
ROLL_BACK(mode_tempstate);
ROLL_BACK(classtype);
}
#else
void
player::ServerInputFrame(void)
{
super::ServerInputFrame();
gflags &= ~GF_NOBUILDZONE;
gflags &= ~GF_NOGRENADEZONE;
}
void
player::EvaluateEntity(void)
{
/* the generic client attributes */
NSClientPlayer::EvaluateEntity();
/* animation */
if (ATTR_CHANGED(anim_bottom) || ATTR_CHANGED(anim_bottom_time))
SendFlags |= PLAYER_BOTTOMFRAME;
if (ATTR_CHANGED(anim_top) || ATTR_CHANGED(anim_top_time) || ATTR_CHANGED(anim_top_delay))
SendFlags |= PLAYER_TOPFRAME;
/* ammo 1 type updates */
if (ATTR_CHANGED(mag_sbs))
SendFlags |= PLAYER_AMMO1;
else if (ATTR_CHANGED(mag_dbs))
SendFlags |= PLAYER_AMMO1;
else if (ATTR_CHANGED(mag_rpg))
SendFlags |= PLAYER_AMMO1;
else if (ATTR_CHANGED(mag_glauncher))
SendFlags |= PLAYER_AMMO1;
/* ammo 2 type updates */
if (ATTR_CHANGED(m_iAmmoRockets))
SendFlags |= PLAYER_AMMO2;
else if (ATTR_CHANGED(m_iAmmoNails))
SendFlags |= PLAYER_AMMO2;
else if (ATTR_CHANGED(m_iAmmoCells))
SendFlags |= PLAYER_AMMO2;
else if (ATTR_CHANGED(m_iAmmoShells))
SendFlags |= PLAYER_AMMO2;
else if (ATTR_CHANGED(m_iAmmoDetpack))
SendFlags |= PLAYER_AMMO2;
else if (ATTR_CHANGED(m_iAmmoMedikit))
SendFlags |= PLAYER_AMMO2;
if (ATTR_CHANGED(mode_tempstate))
SendFlags |= PLAYER_AMMO3;
if (ATTR_CHANGED(classtype))
SendFlags |= PLAYER_AMMO3;
SAVE_STATE(mag_sbs);
SAVE_STATE(mag_dbs);
SAVE_STATE(mag_rpg);
SAVE_STATE(mag_glauncher);
SAVE_STATE(m_iAmmoRockets);
SAVE_STATE(m_iAmmoNails);
SAVE_STATE(m_iAmmoCells);
SAVE_STATE(m_iAmmoShells);
SAVE_STATE(m_iAmmoDetpack);
SAVE_STATE(m_iAmmoMedikit);
SAVE_STATE(mode_tempstate);
SAVE_STATE(classtype);
SAVE_STATE(anim_top);
SAVE_STATE(anim_top_delay);
SAVE_STATE(anim_top_time);
SAVE_STATE(anim_bottom);
SAVE_STATE(anim_bottom_time);
}
void
player::SpawnIntoGame(void)
{
entity spot = world;
/* spawn into the world */
switch (team) {
case 1:
spot = Spawn_SelectRandom("info_teamspawn_blue");
break;
case 2:
spot = Spawn_SelectRandom("info_teamspawn_red");
break;
case 3:
spot = Spawn_SelectRandom("info_teamspawn_yellow");
break;
case 4:
spot = Spawn_SelectRandom("info_teamspawn_green");
break;
}
setorigin(this, spot.origin);
angles = spot.angles;
fixangle = TRUE;
}
void
player::MakeClass(classtype_e class)
{
health = self.max_health = 100;
takedamage = DAMAGE_YES;
solid = SOLID_SLIDEBOX;
movetype = MOVETYPE_WALK;
flags = FL_CLIENT;
viewzoom = 1.0;
/* select our class model */
model = g_teammodels[classtype];
setmodel(this, model);
setsize(this, VEC_HULL_MIN, VEC_HULL_MAX);
velocity = [0,0,0];
gravity = __NULL__;
armor = activeweapon = g_items = 0;
iBleeds = TRUE;
forceinfokey(this, "*spec", "0");
forceinfokey(this, "*team", ftos(team));
switch (classtype) {
case CLASS_SCOUT:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_NAILGUN, -1);
m_iAmmoShells = 17;
m_iAmmoNails = 100;
m_iMaxHealth = 75;
m_iMaxArmor = 50;
health = m_iMaxHealth;
armor = 25;
m_iMaxShells = 50;
m_iMaxNails = 200;
m_iMaxCells = 100;
m_iMaxRockets = 25;
env_message_single(this, "HELP_SCOUT");
break;
case CLASS_SNIPER:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SNIPER, -1);
Weapons_AddItem(this, WEAPON_AUTORIFLE, -1);
Weapons_AddItem(this, WEAPON_NAILGUN, -1);
m_iAmmoShells = 60; /* sniper rifles use shells */
m_iAmmoNails = 50;
m_iMaxHealth = 90;
m_iMaxArmor = 50;
health = m_iMaxHealth;
armor = 0;
m_iMaxShells = 75;
m_iMaxNails = 100;
m_iMaxCells = 50;
m_iMaxRockets = 25;
env_message_single(this, "HELP_SNIPER");
break;
case CLASS_SOLDIER:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_DBS, -1);
Weapons_AddItem(this, WEAPON_RPG, -1);
m_iAmmoShells = 26;
m_iAmmoRockets = 6;
m_iMaxHealth = 100;
m_iMaxArmor = 200;
health = m_iMaxHealth;
armor = 100;
m_iMaxShells = 100;
m_iMaxNails = 100;
m_iMaxCells = 50;
m_iMaxRockets = 50;
env_message_single(this, "HELP_SOLDIER");
break;
case CLASS_DEMO:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_GLAUNCHER, -1);
Weapons_AddItem(this, WEAPON_PIPEBOMB, -1);
m_iAmmoShells = 22;
m_iAmmoRockets = 14;
m_iMaxHealth = 90;
m_iMaxArmor = 100;
health = m_iMaxHealth;
armor = 50;
m_iMaxShells = 75;
m_iMaxNails = 50;
m_iMaxCells = 50;
m_iMaxRockets = 50;
env_message_single(this, "HELP_DEMOMAN");
break;
case CLASS_MEDIC:
Weapons_AddItem(this, WEAPON_MEDKIT, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_DBS, -1);
Weapons_AddItem(this, WEAPON_SUPERNAIL, -1);
m_iAmmoShells = 26;
m_iAmmoNails = 50;
m_iMaxHealth = 90;
m_iMaxArmor = 100;
health = m_iMaxHealth;
armor = 50;
m_iMaxShells = 75;
m_iMaxNails = 150;
m_iMaxCells = 50;
m_iMaxRockets = 25;
env_message_single(this, "HELP_MEDIC");
break;
case CLASS_HVYWEAPON:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_DBS, -1);
Weapons_AddItem(this, WEAPON_ASSCAN, -1);
m_iAmmoShells = 176; /* all of the heavy's weapons use shells */
m_iMaxHealth = 100;
m_iMaxArmor = 300;
health = m_iMaxHealth;
armor = 150;
m_iMaxShells = 200;
m_iMaxNails = 200;
m_iMaxCells = 50;
m_iMaxRockets = 25;
env_message_single(this, "HELP_HWGUY");
break;
case CLASS_PYRO:
Weapons_AddItem(this, WEAPON_CROWBAR, -1);
Weapons_AddItem(this, WEAPON_SBS, -1);
Weapons_AddItem(this, WEAPON_FLAMER, -1);
Weapons_AddItem(this, WEAPON_INCENDIARY, -1);
m_iAmmoShells = 12;
m_iAmmoCells = 120;
m_iAmmoRockets = 5;
m_iMaxHealth = 100;
m_iMaxArmor = 150;
health = m_iMaxHealth;
armor = 50;
m_iMaxShells = 40;
m_iMaxNails = 50;
m_iMaxCells = 200;
m_iMaxRockets = 60;
env_message_single(this, "HELP_PYRO");
break;
case CLASS_SPY:
Weapons_AddItem(this, WEAPON_KNIFE, -1);
Weapons_AddItem(this, WEAPON_TRANQUIL, -1);
Weapons_AddItem(this, WEAPON_DBS, -1);
Weapons_AddItem(this, WEAPON_NAILGUN, -1);
m_iAmmoShells = 24; /* tranquil and dbs use shells */
m_iAmmoNails = 50;
m_iMaxHealth = 90;
m_iMaxArmor = 100;
health = m_iMaxHealth;
armor = 25;
m_iMaxShells = 40;
m_iMaxNails = 50;
m_iMaxCells = 30;
m_iMaxRockets = 15;
env_message_single(this, "HELP_SPY");
break;
case CLASS_ENGINEER:
Weapons_AddItem(this, WEAPON_WRENCH, -1);
Weapons_AddItem(this, WEAPON_RAILGUN, -1);
Weapons_AddItem(this, WEAPON_DBS, -1);
m_iAmmoCells = 100;
m_iAmmoNails = 25;
m_iAmmoShells = 4;
m_iMaxHealth = 80;
m_iMaxArmor = 50;
health = m_iMaxHealth;
armor = 25;
m_iMaxShells = 50;
m_iMaxNails = 50;
m_iMaxCells = 200;
m_iMaxRockets = 30;
env_message_single(this, "HELP_ENGINEER");
break;
}
g_items |= ITEM_SUIT;
}
/*
=================
player::SendEntity
=================
*/
float
player::SendEntity(entity ePEnt, float flChanged)
{
/* don't broadcast invisible players */
if (IsFakeSpectator() && ePEnt != this)
return (0);
if (!GetModelindex() && ePEnt != this)
return (0);
flChanged = OptimiseChangedFlags(ePEnt, flChanged);
WriteByte(MSG_ENTITY, ENT_PLAYER);
WriteFloat(MSG_ENTITY, flChanged);
/* the generic client attributes */
NSClientPlayer::SendEntity(ePEnt, flChanged);
if (flChanged & PLAYER_TOPFRAME) {
WriteByte(MSG_ENTITY, anim_top);
WriteFloat(MSG_ENTITY, anim_top_time);
WriteFloat(MSG_ENTITY, anim_top_delay);
}
if (flChanged & PLAYER_BOTTOMFRAME) {
WriteByte(MSG_ENTITY, anim_bottom);
WriteFloat(MSG_ENTITY, anim_bottom_time);
}
if (flChanged & PLAYER_AMMO1) {
WriteByte(MSG_ENTITY, mag_sbs);
WriteByte(MSG_ENTITY, mag_dbs);
WriteByte(MSG_ENTITY, mag_rpg);
WriteByte(MSG_ENTITY, mag_glauncher);
}
if (flChanged & PLAYER_AMMO2) {
WriteByte(MSG_ENTITY, m_iAmmoRockets);
WriteByte(MSG_ENTITY, m_iAmmoNails);
WriteByte(MSG_ENTITY, m_iAmmoCells);
WriteByte(MSG_ENTITY, m_iAmmoShells);
WriteByte(MSG_ENTITY, m_iAmmoDetpack);
WriteByte(MSG_ENTITY, m_iAmmoMedikit);
}
if (flChanged & PLAYER_AMMO3) {
WriteByte(MSG_ENTITY, mode_tempstate);
WriteByte(MSG_ENTITY, classtype);
}
return (1);
}
#endif