Forgot to add these files (NSClient/Player/Spectator)
This commit is contained in:
parent
25c54af62c
commit
6653d18417
6 changed files with 1743 additions and 0 deletions
38
src/shared/NSClient.h
Normal file
38
src/shared/NSClient.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* both NSClientPlayer and base_NSClientSpectator are based off this class */
|
||||
class
|
||||
NSClient:NSSurfacePropEntity
|
||||
{
|
||||
vector origin_net;
|
||||
vector velocity_net;
|
||||
|
||||
NSXRSpace m_xrSpace;
|
||||
NSXRInput m_xrInputHead;
|
||||
NSXRInput m_xrInputLeft;
|
||||
NSXRInput m_xrInputRight;
|
||||
|
||||
void(void) NSClient;
|
||||
|
||||
/* final input handling of the client */
|
||||
virtual void(void) ClientInput;
|
||||
|
||||
virtual void(void) PreFrame;
|
||||
virtual void(void) PostFrame;
|
||||
|
||||
virtual bool(void) IsFakeSpectator;
|
||||
virtual bool(void) IsRealSpectator;
|
||||
virtual bool(void) IsDead;
|
||||
virtual bool(void) IsPlayer;
|
||||
|
||||
virtual void(void) OnRemoveEntity;
|
||||
|
||||
#ifdef CLIENT
|
||||
/* gives the chance to override input variables before networking */
|
||||
virtual void(void) ClientInputFrame;
|
||||
|
||||
/* our camera when we're dead */
|
||||
virtual void(void) UpdateDeathcam;
|
||||
|
||||
/* run every frame before renderscene() */
|
||||
virtual float(void) predraw;
|
||||
#endif
|
||||
};
|
84
src/shared/NSClient.qc
Normal file
84
src/shared/NSClient.qc
Normal file
|
@ -0,0 +1,84 @@
|
|||
void
|
||||
NSClient::OnRemoveEntity(void)
|
||||
{
|
||||
XR_Shutdown(this);
|
||||
}
|
||||
|
||||
void
|
||||
NSClient::ClientInput(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSClient::PreFrame(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSClient::PostFrame(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
NSClient::IsFakeSpectator(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClient::IsRealSpectator(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClient::IsDead(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClient::IsPlayer(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
void
|
||||
NSClient::ClientInputFrame(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSClient::UpdateDeathcam(void)
|
||||
{
|
||||
/* death cam */
|
||||
view_angles[2] = 45.0f;
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
||||
setproperty(VF_CL_VIEWANGLES, view_angles);
|
||||
setproperty(VF_ANGLES, view_angles);
|
||||
}
|
||||
|
||||
float
|
||||
NSClient::predraw(void)
|
||||
{
|
||||
return (PREDRAW_NEXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
NSClient::NSClient(void)
|
||||
{
|
||||
XR_Init(this);
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
Client_InIntermission(void)
|
||||
{
|
||||
#ifdef CLIENT
|
||||
return g_iIntermission;
|
||||
#else
|
||||
return (float)g_grMode.InIntermission();
|
||||
#endif
|
||||
}
|
122
src/shared/NSClientPlayer.h
Normal file
122
src/shared/NSClientPlayer.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class
|
||||
NSClientPlayer:NSClientSpectator
|
||||
{
|
||||
#ifdef SERVER
|
||||
PREDICTED_INT_N(weaponframe);
|
||||
#else
|
||||
PREDICTED_INT(weaponframe);
|
||||
PREDICTED_FLOAT(vehicle_entnum);
|
||||
#endif
|
||||
PREDICTED_FLOAT(health);
|
||||
PREDICTED_FLOAT(armor);
|
||||
|
||||
PREDICTED_FLOAT_N(colormap);
|
||||
PREDICTED_FLOAT_N(gflags);
|
||||
PREDICTED_FLOAT(viewzoom);
|
||||
PREDICTED_VECTOR_N(view_ofs);
|
||||
PREDICTED_VECTOR(v_angle);
|
||||
PREDICTED_FLOAT_N(pmove_flags);
|
||||
|
||||
PREDICTED_FLOAT(w_attack_next);
|
||||
PREDICTED_FLOAT(w_idle_next);
|
||||
PREDICTED_FLOAT(teleport_time);
|
||||
PREDICTED_FLOAT(weapontime);
|
||||
PREDICTED_VECTOR(punchangle);
|
||||
|
||||
/* We can't use the default .items field, because FTE will assume
|
||||
* effects of some bits. Such as invisibility, quad, etc.
|
||||
* also, modders probably want 32 bits for items. */
|
||||
PREDICTED_INT(g_items);
|
||||
PREDICTED_FLOAT(activeweapon);
|
||||
|
||||
/* vehicle info */
|
||||
PREDICTED_ENT(vehicle);
|
||||
|
||||
/* these are NOT networked */
|
||||
int a_ammo1;
|
||||
int a_ammo2;
|
||||
int a_ammo3;
|
||||
|
||||
/* any mods that use hooks */
|
||||
entity hook;
|
||||
|
||||
void(void) NSClientPlayer;
|
||||
|
||||
virtual void(void) ClientInput;
|
||||
|
||||
virtual void(void) PreFrame;
|
||||
virtual void(void) PostFrame;
|
||||
|
||||
virtual void(float) Physics_Fall;
|
||||
virtual void(void) Physics_Crouch;
|
||||
virtual void(void) Physics_Jump;
|
||||
virtual void(float) Physics_CheckJump;
|
||||
virtual void(void) Physics_SetViewParms;
|
||||
virtual void(void) Physics_WaterJump;
|
||||
virtual void(void) Physics_WaterMove;
|
||||
virtual float(void) Physics_MaxSpeed;
|
||||
virtual void(void) Physics_InputPreMove;
|
||||
virtual void(void) Physics_InputPostMove;
|
||||
virtual void(void) Physics_Run;
|
||||
|
||||
virtual bool(void) IsFakeSpectator;
|
||||
virtual bool(void) IsRealSpectator;
|
||||
virtual bool(void) IsDead;
|
||||
virtual bool(void) IsPlayer;
|
||||
|
||||
#ifdef CLIENT
|
||||
int sequence;
|
||||
|
||||
/* external weapon model */
|
||||
entity p_model;
|
||||
int p_hand_bone;
|
||||
int p_model_bone;
|
||||
float lastweapon;
|
||||
|
||||
virtual void(void) VehicleRelink;
|
||||
virtual void(void) OnRemoveEntity;
|
||||
virtual void(float, float) ReceiveEntity;
|
||||
virtual void(void) PredictPreFrame;
|
||||
virtual void(void) PredictPostFrame;
|
||||
virtual void(void) ClientInputFrame;
|
||||
#else
|
||||
int voted;
|
||||
int step;
|
||||
float step_time;
|
||||
|
||||
float underwater_time;
|
||||
float underwater_dmg;
|
||||
float pain_time;
|
||||
|
||||
entity last_used;
|
||||
|
||||
virtual void(float) Save;
|
||||
virtual void(string,string) Restore;
|
||||
virtual void(void) Respawn;
|
||||
virtual void(void) EvaluateEntity;
|
||||
virtual float(entity, float) SendEntity;
|
||||
|
||||
virtual void(void) Death;
|
||||
virtual void(void) MakePlayer;
|
||||
virtual void(void) MakeTempSpectator;
|
||||
|
||||
virtual void(void) InputUse_Down;
|
||||
virtual void(void) InputUse_Up;
|
||||
#endif
|
||||
};
|
951
src/shared/NSClientPlayer.qc
Normal file
951
src/shared/NSClientPlayer.qc
Normal file
|
@ -0,0 +1,951 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
bool
|
||||
NSClientPlayer::IsRealSpectator(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientPlayer::IsDead(void)
|
||||
{
|
||||
if (health > 0)
|
||||
return (false);
|
||||
else
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientPlayer::IsPlayer(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientPlayer::IsFakeSpectator(void)
|
||||
{
|
||||
if (GetFlags() & FL_FAKESPEC)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
NSClientPlayer::PreFrame(void)
|
||||
{
|
||||
#ifdef CLIENT
|
||||
/* this is where a game/mod would decide to add more prediction rollback
|
||||
* information. */
|
||||
PredictPreFrame();
|
||||
|
||||
if (vehicle) {
|
||||
NSVehicle veh = (NSVehicle)vehicle;
|
||||
veh.PredictPreFrame();
|
||||
}
|
||||
|
||||
/* run physics code for all the input frames which we've not heard back
|
||||
* from yet. This continues on in Player_ReceiveEntity! */
|
||||
for (int i = sequence + 1; i <= clientcommandframe; i++) {
|
||||
float flSuccess = getinputstate(i);
|
||||
if (flSuccess == FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* don't do partial frames, aka incomplete input packets */
|
||||
if (input_timelength == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i==clientcommandframe){
|
||||
CSQC_Input_Frame();
|
||||
}
|
||||
|
||||
/* this global is for our shared random number seed */
|
||||
input_sequence = i;
|
||||
|
||||
/* run our custom physics */
|
||||
Physics_Run();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void
|
||||
NSClientPlayer::PostFrame(void)
|
||||
{
|
||||
#ifdef CLIENT
|
||||
/* give the game/mod a chance to roll back its values too */
|
||||
PredictPostFrame();
|
||||
setorigin(this, origin); /* update bounds */
|
||||
|
||||
if (vehicle) {
|
||||
NSVehicle veh = (NSVehicle)vehicle;
|
||||
veh.PredictPostFrame();
|
||||
setorigin(veh, veh.origin);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
NSClientPlayer::ClientInput(void)
|
||||
{
|
||||
XR_InputFrame(this);
|
||||
|
||||
if (!Client_InIntermission() && IsFakeSpectator()) {
|
||||
NSClientSpectator::ClientInput();
|
||||
SpectatorTrackPlayer();
|
||||
return;
|
||||
}
|
||||
|
||||
/* allow vehicles to prevent weapon logic from happening */
|
||||
if (vehicle) {
|
||||
NSVehicle veh = (NSVehicle)vehicle;
|
||||
|
||||
if (veh.PlayerInput)
|
||||
veh.PlayerInput();
|
||||
}
|
||||
|
||||
/* weapon/item logic of what the player controls */
|
||||
Game_Input((player)this);
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
void
|
||||
NSClientPlayer::VehicleRelink(void)
|
||||
{
|
||||
if (!vehicle_entnum)
|
||||
vehicle = __NULL__;
|
||||
else
|
||||
vehicle = findentity(world, ::entnum, vehicle_entnum);
|
||||
}
|
||||
|
||||
void
|
||||
NSClientPlayer::OnRemoveEntity(void)
|
||||
{
|
||||
if (p_model)
|
||||
remove(p_model);
|
||||
|
||||
super::OnRemoveEntity();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::ClientInputFrame
|
||||
|
||||
This is basically CSQC_Input_Frame! So games can override this as they please.
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::ClientInputFrame(void)
|
||||
{
|
||||
if (IsFakeSpectator()) {
|
||||
NSClientSpectator::ClientInputFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we are inside a VGUI, don't let the client do stuff outside */
|
||||
if (VGUI_Active()) {
|
||||
input_impulse = 0;
|
||||
input_buttons = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* background maps have no input */
|
||||
if (serverkeyfloat("background") == 1)
|
||||
return;
|
||||
|
||||
if (pSeat->m_iInputAttack2 == TRUE) {
|
||||
input_buttons |= INPUT_BUTTON3;
|
||||
}
|
||||
|
||||
if (pSeat->m_iInputReload == TRUE) {
|
||||
input_buttons |= INPUT_BUTTON4;
|
||||
}
|
||||
|
||||
if (pSeat->m_iInputUse == TRUE) {
|
||||
input_buttons |= INPUT_BUTTON5;
|
||||
}
|
||||
|
||||
if (pSeat->m_iInputDuck == TRUE) {
|
||||
input_buttons |= INPUT_BUTTON8;
|
||||
}
|
||||
|
||||
/* The HUD needs more time */
|
||||
if (pSeat->m_iHUDWeaponSelected) {
|
||||
if ((input_buttons & INPUT_BUTTON0))
|
||||
HUD_DrawWeaponSelect_Trigger();
|
||||
else if ((input_buttons & INPUT_BUTTON3))
|
||||
pSeat->m_iHUDWeaponSelected = pSeat->m_flHUDWeaponSelectTime = 0;
|
||||
|
||||
pSeat->m_flInputBlockTime = time + 0.2;
|
||||
}
|
||||
|
||||
/* prevent accidental input packets */
|
||||
if (pSeat->m_flInputBlockTime > time) {
|
||||
input_buttons &= ~INPUT_BUTTON0;
|
||||
input_buttons &= ~INPUT_BUTTON3;
|
||||
pSeat->m_iInputAttack2 = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* some input overrides for XR */
|
||||
if (XR_Available(this)) {
|
||||
if (pSeat->m_bMoveForward) {
|
||||
input_movevalues[0] = 100;
|
||||
}
|
||||
|
||||
if (pSeat->m_iInputAttack) {
|
||||
input_buttons |= INPUT_BUTTON0;
|
||||
}
|
||||
}
|
||||
|
||||
/* compat*/
|
||||
if (input_impulse == 201) {
|
||||
sendevent("Spraylogo", "");
|
||||
}
|
||||
|
||||
if (pSeat->m_flCameraTime > time) {
|
||||
/* TODO: Supress the changing of view_angles/input_angles. */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::ReceiveEntity
|
||||
|
||||
Receive the generic client attributes from the server.
|
||||
If you want to override this, do not call this
|
||||
at the top of player::ReceiveEntity
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::ReceiveEntity(float new, float fl)
|
||||
{
|
||||
/* store which input sequence we're on, this helps us
|
||||
* later when we run prediction again between last/latest
|
||||
* servercommandframe */
|
||||
sequence = servercommandframe;
|
||||
|
||||
/* HACK: we need to make this more reliable */
|
||||
if (fl == UPDATE_ALL) {
|
||||
/* we respawned */
|
||||
gravity = 1.0f;
|
||||
}
|
||||
|
||||
if (fl & PLAYER_MODELINDEX) {
|
||||
modelindex = readshort();
|
||||
}
|
||||
|
||||
if (fl & PLAYER_ORIGIN) {
|
||||
origin[0] = readcoord();
|
||||
origin[1] = readcoord();
|
||||
}
|
||||
|
||||
if (fl & PLAYER_ORIGIN_Z)
|
||||
origin[2] = readcoord();
|
||||
if (fl & PLAYER_ANGLES_X) {
|
||||
v_angle[0] = readshort() / (32767 / 360);
|
||||
v_angle[1] = readshort() / (32767 / 360);
|
||||
v_angle[2] = readshort() / (32767 / 360);
|
||||
}
|
||||
if (fl & PLAYER_ANGLES_Y) {
|
||||
angles[0] = readshort() / (32767 / 360);
|
||||
angles[1] = readshort() / (32767 / 360);
|
||||
angles[2] = readshort() / (32767 / 360);
|
||||
}
|
||||
if (fl & PLAYER_COLORMAP)
|
||||
colormap = readbyte();
|
||||
|
||||
if (fl & PLAYER_VELOCITY) {
|
||||
velocity[0] = readcoord();
|
||||
velocity[1] = readcoord();
|
||||
}
|
||||
|
||||
if (fl & PLAYER_VELOCITY_Z)
|
||||
velocity[2] = readcoord();
|
||||
if (fl & PLAYER_FLAGS) {
|
||||
flags = readfloat();
|
||||
gflags = readfloat();
|
||||
pmove_flags = readfloat();
|
||||
|
||||
/* mainly used for other players receiving us */
|
||||
if (flags & FL_CROUCHING)
|
||||
setsize(self, PHY_HULL_CROUCHED_MIN, PHY_HULL_CROUCHED_MAX);
|
||||
else
|
||||
setsize(self, PHY_HULL_MIN, PHY_HULL_MAX);
|
||||
}
|
||||
if (fl & PLAYER_WEAPON) {
|
||||
activeweapon = readbyte();
|
||||
weaponframe = (int)readbyte();
|
||||
}
|
||||
if (fl & PLAYER_ITEMS)
|
||||
g_items = (__variant)readfloat();
|
||||
if (fl & PLAYER_HEALTH)
|
||||
health = readbyte();
|
||||
if (fl & PLAYER_ARMOR)
|
||||
armor = readbyte();
|
||||
if (fl & PLAYER_MOVETYPE) {
|
||||
movetype = readbyte();
|
||||
solid = readbyte();
|
||||
}
|
||||
if (fl & PLAYER_VIEWOFS)
|
||||
view_ofs[2] = readfloat();
|
||||
|
||||
/* TO OPTIMISE */
|
||||
teleport_time = readfloat();
|
||||
viewzoom = readfloat();
|
||||
weapontime = readfloat();
|
||||
w_attack_next = readfloat();
|
||||
w_idle_next = readfloat();
|
||||
punchangle[0] = readfloat();
|
||||
punchangle[1] = readfloat();
|
||||
punchangle[2] = readfloat();
|
||||
vehicle_entnum = readentitynum();
|
||||
VehicleRelink();
|
||||
|
||||
/* FIXME: Make this temp spec only */
|
||||
spec_ent = readbyte();
|
||||
spec_mode = readbyte();
|
||||
spec_flags = readbyte();
|
||||
|
||||
PredictPreFrame();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::PredictPreFrame
|
||||
|
||||
Save the state of the last server-confirmed attributes.
|
||||
If you want to override this, do not call this
|
||||
at the top of player::PredictPreFrame
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::PredictPreFrame(void)
|
||||
{
|
||||
/* base player attributes/fields we're going to roll back */
|
||||
SAVE_STATE(modelindex);
|
||||
SAVE_STATE(origin);
|
||||
SAVE_STATE(angles);
|
||||
SAVE_STATE(v_angle);
|
||||
SAVE_STATE(colormap);
|
||||
SAVE_STATE(velocity);
|
||||
SAVE_STATE(flags);
|
||||
SAVE_STATE(gflags);
|
||||
SAVE_STATE(pmove_flags);
|
||||
SAVE_STATE(activeweapon);
|
||||
SAVE_STATE(g_items);
|
||||
SAVE_STATE(health);
|
||||
SAVE_STATE(armor);
|
||||
SAVE_STATE(movetype);
|
||||
SAVE_STATE(solid);
|
||||
SAVE_STATE(view_ofs);
|
||||
|
||||
/* TO OPTIMISE */
|
||||
SAVE_STATE(teleport_time);
|
||||
SAVE_STATE(viewzoom);
|
||||
SAVE_STATE(weaponframe);
|
||||
SAVE_STATE(weapontime);
|
||||
SAVE_STATE(w_attack_next);
|
||||
SAVE_STATE(w_idle_next);
|
||||
SAVE_STATE(punchangle);
|
||||
SAVE_STATE(vehicle_entnum);
|
||||
|
||||
SAVE_STATE(spec_ent);
|
||||
SAVE_STATE(spec_mode);
|
||||
SAVE_STATE(spec_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::PredictPostFrame
|
||||
|
||||
After running prediction on the client, roll back the values
|
||||
to the server's confirmed saved attributes from PredictPreFrame.
|
||||
If you want to override this, do not call this
|
||||
at the top of player::PredictPostFrame
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::PredictPostFrame(void)
|
||||
{
|
||||
/* finally roll the values back */
|
||||
ROLL_BACK(modelindex);
|
||||
ROLL_BACK(origin);
|
||||
ROLL_BACK(angles);
|
||||
ROLL_BACK(v_angle);
|
||||
ROLL_BACK(colormap);
|
||||
ROLL_BACK(velocity);
|
||||
ROLL_BACK(flags);
|
||||
ROLL_BACK(gflags);
|
||||
ROLL_BACK(pmove_flags);
|
||||
ROLL_BACK(activeweapon);
|
||||
ROLL_BACK(g_items);
|
||||
ROLL_BACK(health);
|
||||
ROLL_BACK(armor);
|
||||
ROLL_BACK(movetype);
|
||||
ROLL_BACK(solid);
|
||||
ROLL_BACK(view_ofs);
|
||||
|
||||
/* TO OPTIMISE */
|
||||
ROLL_BACK(teleport_time);
|
||||
ROLL_BACK(viewzoom);
|
||||
ROLL_BACK(weaponframe);
|
||||
ROLL_BACK(weapontime);
|
||||
ROLL_BACK(w_attack_next);
|
||||
ROLL_BACK(w_idle_next);
|
||||
ROLL_BACK(punchangle);
|
||||
ROLL_BACK(vehicle_entnum);
|
||||
|
||||
ROLL_BACK(spec_ent);
|
||||
ROLL_BACK(spec_mode);
|
||||
ROLL_BACK(spec_flags);
|
||||
}
|
||||
#else
|
||||
void
|
||||
NSClientPlayer::Save(float handle)
|
||||
{
|
||||
SaveFloat(handle, "health", health);
|
||||
SaveFloat(handle, "armor", armor);
|
||||
SaveFloat(handle, "modelindex", modelindex);
|
||||
SaveVector(handle, "origin", origin);
|
||||
SaveVector(handle, "velocity", velocity);
|
||||
SaveVector(handle, "angles", angles);
|
||||
SaveFloat(handle, "colormap", colormap);
|
||||
SaveFloat(handle, "flags", flags);
|
||||
SaveFloat(handle, "gflags", gflags);
|
||||
SaveFloat(handle, "viewzoom", viewzoom);
|
||||
SaveVector(handle, "view_ofs", view_ofs);
|
||||
SaveVector(handle, "v_angle", v_angle);
|
||||
SaveVector(handle, "punchangle", punchangle);
|
||||
SaveFloat(handle, "movetype", movetype);
|
||||
SaveFloat(handle, "pmove_flags", pmove_flags);
|
||||
SaveFloat(handle, "w_attack_next", w_attack_next);
|
||||
SaveFloat(handle, "w_idle_next", w_idle_next);
|
||||
SaveFloat(handle, "teleport_time", teleport_time);
|
||||
SaveInt(handle, "weaponframe", weaponframe);
|
||||
SaveFloat(handle, "weapontime", weapontime);
|
||||
SaveInt(handle, "g_items", g_items);
|
||||
SaveFloat(handle, "activeweapon", activeweapon);
|
||||
SaveFloat(handle, "vehicle", num_for_edict(vehicle));
|
||||
}
|
||||
|
||||
void
|
||||
NSClientPlayer::Restore(string strKey, string strValue)
|
||||
{
|
||||
switch (strKey) {
|
||||
case "health":
|
||||
health = ReadFloat(strValue);
|
||||
break;
|
||||
case "armor":
|
||||
armor = ReadFloat(strValue);
|
||||
break;
|
||||
case "modelindex":
|
||||
modelindex = ReadFloat(strValue);
|
||||
break;
|
||||
case "origin":
|
||||
origin = ReadVector(strValue);
|
||||
break;
|
||||
case "velocity":
|
||||
velocity = ReadVector(strValue);
|
||||
break;
|
||||
case "angles":
|
||||
angles = ReadVector(strValue);
|
||||
break;
|
||||
case "colormap":
|
||||
colormap = ReadFloat(strValue);
|
||||
break;
|
||||
case "flags":
|
||||
flags = ReadFloat(strValue);
|
||||
break;
|
||||
case "gflags":
|
||||
gflags = ReadFloat(strValue);
|
||||
break;
|
||||
case "view_ofs":
|
||||
view_ofs = ReadVector(strValue);
|
||||
break;
|
||||
case "v_angle":
|
||||
v_angle = ReadVector(strValue);
|
||||
break;
|
||||
case "punchangle":
|
||||
punchangle = ReadVector(strValue);
|
||||
break;
|
||||
case "movetype":
|
||||
movetype = ReadFloat(strValue);
|
||||
break;
|
||||
case "pmove_flags":
|
||||
pmove_flags = ReadFloat(strValue);
|
||||
break;
|
||||
case "w_attack_next":
|
||||
w_attack_next = ReadFloat(strValue);
|
||||
break;
|
||||
case "w_idle_next":
|
||||
w_idle_next = ReadFloat(strValue);
|
||||
break;
|
||||
case "teleport_time":
|
||||
teleport_time = ReadFloat(strValue);
|
||||
break;
|
||||
case "weaponframe":
|
||||
weaponframe = ReadInt(strValue);
|
||||
break;
|
||||
case "weapontime":
|
||||
weapontime = ReadFloat(strValue);
|
||||
break;
|
||||
case "g_items":
|
||||
g_items = ReadInt(strValue);
|
||||
break;
|
||||
case "activeweapon":
|
||||
activeweapon = ReadFloat(strValue);
|
||||
break;
|
||||
case "vehicle":
|
||||
vehicle = edict_num(ReadFloat(strValue));
|
||||
break;
|
||||
default:
|
||||
super::Restore(strKey, strValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::Respawn
|
||||
|
||||
it'd be pretty unfortunate if 'sv respawn_ents' or something called this
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::Respawn(void)
|
||||
{
|
||||
/* make sure nothing happens here */
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::MakeTempSpectator
|
||||
|
||||
This is what dead players in round matches become, or when we spawn
|
||||
for the first time before selecting a loadout or something.
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::MakeTempSpectator(void)
|
||||
{
|
||||
classname = "player";
|
||||
flags = FL_CLIENT;
|
||||
SetModelindex(0);
|
||||
SetSolid(SOLID_NOT);
|
||||
SetMovetype(MOVETYPE_NOCLIP);
|
||||
SetTakedamage(DAMAGE_NO);
|
||||
maxspeed = 250;
|
||||
flags |= FL_FAKESPEC;
|
||||
max_health = health = 0;
|
||||
armor = 0;
|
||||
g_items = 0;
|
||||
activeweapon = 0;
|
||||
effects = 0;
|
||||
alpha = 0.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::MakeDead
|
||||
|
||||
Sets all the appropriate attributes to make sure we're dead
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::Death(void)
|
||||
{
|
||||
classname = "player";
|
||||
health = max_health = 0;
|
||||
armor = 0;
|
||||
g_items = 0;
|
||||
activeweapon = 0;
|
||||
effects = 0;
|
||||
alpha = 1.0f;
|
||||
SetModelindex(0);
|
||||
SetMovetype(MOVETYPE_NONE);
|
||||
SetSolid(SOLID_NOT);
|
||||
SetTakedamage(DAMAGE_NO);
|
||||
viewzoom = 1.0;
|
||||
view_ofs = [0,0,0];
|
||||
vehicle = __NULL__;
|
||||
SetVelocity([0,0,0]);
|
||||
SetGravity(1.0f);
|
||||
customphysics = Empty;
|
||||
iBleeds = FALSE;
|
||||
forceinfokey(this, "*deaths", ftos(deaths));
|
||||
setsize(this, [0,0,0], [0,0,0]);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::MakePlayer
|
||||
|
||||
True participating player, can walk around and everything.
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::MakePlayer(void)
|
||||
{
|
||||
classname = "player";
|
||||
flags = FL_CLIENT;
|
||||
health = max_health = 100;
|
||||
armor = 0;
|
||||
g_items = 0;
|
||||
activeweapon = 0;
|
||||
effects = 0;
|
||||
alpha = 1.0f;
|
||||
SetSolid(SOLID_SLIDEBOX);
|
||||
SetMovetype(MOVETYPE_WALK);
|
||||
SetTakedamage(DAMAGE_YES);
|
||||
SetVelocity([0,0,0]);
|
||||
viewzoom = 1.0;
|
||||
vehicle = __NULL__;
|
||||
SetGravity(1.0f);
|
||||
SendFlags = UPDATE_ALL;
|
||||
customphysics = Empty;
|
||||
iBleeds = TRUE;
|
||||
forceinfokey(this, "*deaths", ftos(deaths));
|
||||
SetSize(VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::EvaluateEntity
|
||||
|
||||
Check which attributes have changed and flag the ones that did.
|
||||
If you want to override this, do not call this
|
||||
at the top of player::EvaluateEntity
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::EvaluateEntity(void)
|
||||
{
|
||||
SetSendFlags(PLAYER_KEEPALIVE);
|
||||
|
||||
if (ATTR_CHANGED(modelindex))
|
||||
SetSendFlags(PLAYER_MODELINDEX);
|
||||
|
||||
if (VEC_CHANGED(origin, 0))
|
||||
SetSendFlags(PLAYER_ORIGIN);
|
||||
|
||||
if (VEC_CHANGED(origin, 1))
|
||||
SetSendFlags(PLAYER_ORIGIN);
|
||||
|
||||
if (VEC_CHANGED(origin, 2))
|
||||
SetSendFlags(PLAYER_ORIGIN_Z);
|
||||
|
||||
if (VEC_CHANGED(v_angle, 0) || VEC_CHANGED(v_angle, 1) || VEC_CHANGED(v_angle, 2))
|
||||
SetSendFlags(PLAYER_ANGLES_X);
|
||||
|
||||
if (VEC_CHANGED(angles, 0) || VEC_CHANGED(angles, 1) || VEC_CHANGED(angles, 2))
|
||||
SetSendFlags(PLAYER_ANGLES_Y);
|
||||
|
||||
if (ATTR_CHANGED(colormap))
|
||||
SetSendFlags(PLAYER_COLORMAP);
|
||||
|
||||
if (VEC_CHANGED(velocity, 0))
|
||||
SetSendFlags(PLAYER_VELOCITY);
|
||||
|
||||
if (VEC_CHANGED(velocity, 1))
|
||||
SetSendFlags(PLAYER_VELOCITY);
|
||||
|
||||
if (VEC_CHANGED(velocity, 2))
|
||||
SetSendFlags(PLAYER_VELOCITY_Z);
|
||||
|
||||
if (ATTR_CHANGED(flags))
|
||||
SetSendFlags(PLAYER_FLAGS);
|
||||
|
||||
if (ATTR_CHANGED(gflags))
|
||||
SetSendFlags(PLAYER_FLAGS);
|
||||
|
||||
if (ATTR_CHANGED(pmove_flags))
|
||||
SetSendFlags(PLAYER_FLAGS);
|
||||
|
||||
if (ATTR_CHANGED(weaponframe))
|
||||
SetSendFlags(PLAYER_WEAPON);
|
||||
|
||||
if (ATTR_CHANGED(activeweapon))
|
||||
SetSendFlags(PLAYER_WEAPON);
|
||||
|
||||
if (ATTR_CHANGED(g_items))
|
||||
SetSendFlags(PLAYER_ITEMS);
|
||||
|
||||
if (ATTR_CHANGED(health))
|
||||
SetSendFlags(PLAYER_HEALTH);
|
||||
|
||||
if (ATTR_CHANGED(armor))
|
||||
SetSendFlags(PLAYER_ARMOR);
|
||||
|
||||
if (ATTR_CHANGED(movetype))
|
||||
SetSendFlags(PLAYER_MOVETYPE);
|
||||
|
||||
if (ATTR_CHANGED(solid))
|
||||
SetSendFlags(PLAYER_MOVETYPE);
|
||||
|
||||
if (ATTR_CHANGED(view_ofs))
|
||||
SetSendFlags(PLAYER_VIEWOFS);
|
||||
|
||||
SAVE_STATE(modelindex);
|
||||
SAVE_STATE(origin);
|
||||
SAVE_STATE(angles);
|
||||
SAVE_STATE(colormap);
|
||||
SAVE_STATE(velocity);
|
||||
SAVE_STATE(flags);
|
||||
SAVE_STATE(gflags);
|
||||
SAVE_STATE(pmove_flags);
|
||||
SAVE_STATE(activeweapon);
|
||||
SAVE_STATE(g_items);
|
||||
SAVE_STATE(health);
|
||||
SAVE_STATE(armor);
|
||||
SAVE_STATE(movetype);
|
||||
SAVE_STATE(solid);
|
||||
SAVE_STATE(view_ofs);
|
||||
|
||||
/* TO OPTIMISE */
|
||||
SAVE_STATE(teleport_time);
|
||||
SAVE_STATE(viewzoom);
|
||||
SAVE_STATE(weaponframe);
|
||||
SAVE_STATE(weapontime);
|
||||
SAVE_STATE(w_attack_next);
|
||||
SAVE_STATE(w_idle_next);
|
||||
SAVE_STATE(punchangle);
|
||||
SAVE_STATE(vehicle);
|
||||
|
||||
/* FIXME: Make this temp spec only */
|
||||
SAVE_STATE(spec_ent);
|
||||
SAVE_STATE(spec_mode);
|
||||
SAVE_STATE(spec_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer::SendEntity
|
||||
|
||||
Network any attributes that have been flagged for networking.
|
||||
If you want to override this, do not call this
|
||||
at the top of player::SendEntity
|
||||
=================
|
||||
*/
|
||||
float
|
||||
NSClientPlayer::SendEntity(entity ePEnt, float fChanged)
|
||||
{
|
||||
/* really trying to get our moneys worth with 23 bits of mantissa */
|
||||
if (fChanged & PLAYER_MODELINDEX) {
|
||||
WriteShort(MSG_ENTITY, modelindex);
|
||||
}
|
||||
|
||||
/* if origin[0] changes, it's very likely [1] changes too, since
|
||||
* we rarely ever walk in a straight line on the world grid */
|
||||
if (fChanged & PLAYER_ORIGIN) {
|
||||
WriteCoord(MSG_ENTITY, origin[0]);
|
||||
WriteCoord(MSG_ENTITY, origin[1]);
|
||||
}
|
||||
/* the height doesn't change as much */
|
||||
if (fChanged & PLAYER_ORIGIN_Z)
|
||||
WriteCoord(MSG_ENTITY, origin[2]);
|
||||
|
||||
if (fChanged & PLAYER_ANGLES_X) {
|
||||
WriteShort(MSG_ENTITY, v_angle[0] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, v_angle[1] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, v_angle[2] * 32767 / 360);
|
||||
}
|
||||
if (fChanged & PLAYER_ANGLES_Y) {
|
||||
WriteShort(MSG_ENTITY, angles[0] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, angles[1] * 32767 / 360);
|
||||
WriteShort(MSG_ENTITY, angles[2] * 32767 / 360);
|
||||
}
|
||||
if (fChanged & PLAYER_COLORMAP)
|
||||
WriteByte(MSG_ENTITY, colormap);
|
||||
|
||||
/* similar as with origin, we separate x/y from z */
|
||||
if (fChanged & PLAYER_VELOCITY) {
|
||||
WriteCoord(MSG_ENTITY, velocity[0]);
|
||||
WriteCoord(MSG_ENTITY, velocity[1]);
|
||||
}
|
||||
if (fChanged & PLAYER_VELOCITY_Z)
|
||||
WriteCoord(MSG_ENTITY, velocity[2]);
|
||||
|
||||
if (fChanged & PLAYER_FLAGS) {
|
||||
WriteFloat(MSG_ENTITY, flags);
|
||||
WriteFloat(MSG_ENTITY, gflags);
|
||||
WriteFloat(MSG_ENTITY, pmove_flags);
|
||||
}
|
||||
if (fChanged & PLAYER_WEAPON) {
|
||||
WriteByte(MSG_ENTITY, activeweapon);
|
||||
WriteByte(MSG_ENTITY, weaponframe);
|
||||
}
|
||||
|
||||
/* g_items is a proper integer, so we can't let WriteFloat truncate it (hence __variant) */
|
||||
if (fChanged & PLAYER_ITEMS)
|
||||
WriteFloat(MSG_ENTITY, (__variant)g_items);
|
||||
|
||||
/* only got byte precision, clamp to avoid weird values on the client-side */
|
||||
if (fChanged & PLAYER_HEALTH)
|
||||
WriteByte(MSG_ENTITY, bound(0, health, 255));
|
||||
if (fChanged & PLAYER_ARMOR)
|
||||
WriteByte(MSG_ENTITY, bound(0, armor, 255));
|
||||
|
||||
if (fChanged & PLAYER_MOVETYPE) {
|
||||
WriteByte(MSG_ENTITY, movetype);
|
||||
WriteByte(MSG_ENTITY, solid);
|
||||
}
|
||||
|
||||
/* the view_ofs[0] and [1] are rarely changed */
|
||||
if (fChanged & PLAYER_VIEWOFS)
|
||||
WriteFloat(MSG_ENTITY, view_ofs[2]);
|
||||
|
||||
/* TO OPTIMISE */
|
||||
WriteFloat(MSG_ENTITY, teleport_time);
|
||||
WriteFloat(MSG_ENTITY, viewzoom);
|
||||
WriteFloat(MSG_ENTITY, weapontime);
|
||||
WriteFloat(MSG_ENTITY, w_attack_next);
|
||||
WriteFloat(MSG_ENTITY, w_idle_next);
|
||||
WriteFloat(MSG_ENTITY, punchangle[0]);
|
||||
WriteFloat(MSG_ENTITY, punchangle[1]);
|
||||
WriteFloat(MSG_ENTITY, punchangle[2]);
|
||||
|
||||
if (vehicle)
|
||||
WriteEntity(MSG_ENTITY, vehicle);
|
||||
else
|
||||
WriteEntity(MSG_ENTITY, __NULL__);
|
||||
|
||||
/* FIXME: Make this fake NSClientSpectator only. */
|
||||
WriteByte(MSG_ENTITY, spec_ent);
|
||||
WriteByte(MSG_ENTITY, spec_mode);
|
||||
WriteByte(MSG_ENTITY, spec_flags);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
_NSClientPlayer_useworkaround
|
||||
|
||||
A wrapper to cleanly reset 'self' as to not mess up the QC VM
|
||||
====================
|
||||
*/
|
||||
void
|
||||
_NSClientPlayer_useworkaround(entity eTarget)
|
||||
{
|
||||
eActivator = self;
|
||||
entity eOldSelf = self;
|
||||
self = eTarget;
|
||||
self.PlayerUse();
|
||||
self = eOldSelf;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
_NSClientPlayer_useworkaround
|
||||
|
||||
A wrapper to cleanly reset 'self' as to not mess up the QC VM
|
||||
====================
|
||||
*/
|
||||
void
|
||||
_NSClientPlayer_unuseworkaround(entity eTarget)
|
||||
{
|
||||
eActivator = self;
|
||||
entity eOldSelf = self;
|
||||
self = eTarget;
|
||||
if (self.PlayerUseUnpressed)
|
||||
self.PlayerUseUnpressed();
|
||||
self = eOldSelf;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer:: InputUse_Down
|
||||
|
||||
Called when we hold down the +use button for the first time,
|
||||
looks for an entity that has the .PlayerUse field set to a function and calls it.
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::InputUse_Down(void)
|
||||
{
|
||||
if (health <= 0) {
|
||||
return;
|
||||
} else if (!(flags & FL_USE_RELEASED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector vecSource;
|
||||
entity eRad;
|
||||
bool found_use = false;
|
||||
|
||||
makevectors(v_angle);
|
||||
vecSource = origin + view_ofs;
|
||||
traceline(vecSource, vecSource + (v_forward * 64), MOVE_EVERYTHING, this);
|
||||
|
||||
/* first see if we traced something head-on, else we'll findradius something */
|
||||
if (trace_ent.PlayerUse) {
|
||||
found_use = true;
|
||||
eRad = trace_ent;
|
||||
} else {
|
||||
/* find anything in a 8 unit radius, including certain non-solids (func_door, func_rot_button etc. */
|
||||
eRad = findradius(trace_endpos, 8);
|
||||
|
||||
/* loop through our chain and just pick the first valid one */
|
||||
while (eRad) {
|
||||
if (eRad.PlayerUse) {
|
||||
found_use = true;
|
||||
break;
|
||||
}
|
||||
eRad = eRad.chain;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: maybe eRad will return something in the future that'll suppress a successfull use? */
|
||||
if (eRad && found_use == true) {
|
||||
flags &= ~FL_USE_RELEASED;
|
||||
_NSClientPlayer_useworkaround(eRad);
|
||||
last_used = eRad;
|
||||
|
||||
/* Some entities want to support Use spamming */
|
||||
if (!(flags & FL_USE_RELEASED)) {
|
||||
sound(this, CHAN_ITEM, "common/wpn_select.wav", 0.25, ATTN_IDLE);
|
||||
}
|
||||
} else {
|
||||
sound(this, CHAN_ITEM, "common/wpn_denyselect.wav", 0.25, ATTN_IDLE);
|
||||
flags &= ~FL_USE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NSClientPlayer:: InputUse_Down
|
||||
|
||||
Called when we let go of the +use button
|
||||
=================
|
||||
*/
|
||||
void
|
||||
NSClientPlayer::InputUse_Up(void)
|
||||
{
|
||||
if (!(flags & FL_USE_RELEASED)) {
|
||||
_NSClientPlayer_unuseworkaround(last_used);
|
||||
last_used = world;
|
||||
flags |= FL_USE_RELEASED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
NSClientPlayer::NSClientPlayer(void)
|
||||
{
|
||||
super::NSClientSpectator();
|
||||
vehicle = __NULL__;
|
||||
}
|
65
src/shared/NSClientSpectator.h
Normal file
65
src/shared/NSClientSpectator.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
typedef enumflags
|
||||
{
|
||||
SPECFL_ORIGIN,
|
||||
SPECFL_VELOCITY,
|
||||
SPECFL_TARGET,
|
||||
SPECFL_MODE,
|
||||
SPECFL_FLAGS
|
||||
} NSClientSpectatorFlags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SPECMODE_FREE,
|
||||
SPECMODE_THIRDPERSON,
|
||||
SPECMODE_FIRSTPERSON,
|
||||
SPECMODE_OVERVIEW
|
||||
} NSClientSpectatorMode_t;
|
||||
|
||||
typedef enumflags
|
||||
{
|
||||
SPECFLAG_BUTTON_RELEASED,
|
||||
};
|
||||
|
||||
class NSClientSpectator:NSClient
|
||||
{
|
||||
PREDICTED_FLOAT(spec_ent);
|
||||
PREDICTED_FLOAT(spec_flags);
|
||||
NSClientSpectatorMode_t spec_mode; NSClientSpectatorMode_t spec_mode_net;
|
||||
|
||||
vector spec_org;
|
||||
|
||||
int sequence;
|
||||
|
||||
void(void) NSClientSpectator;
|
||||
|
||||
virtual void(void) ClientInput;
|
||||
|
||||
virtual void(void) InputNext;
|
||||
virtual void(void) InputPrevious;
|
||||
virtual void(void) InputMode;
|
||||
|
||||
virtual void(void) WarpToTarget;
|
||||
|
||||
virtual void(void) PreFrame;
|
||||
virtual void(void) PostFrame;
|
||||
virtual void(void) SpectatorTrackPlayer;
|
||||
|
||||
virtual bool(void) IsFakeSpectator;
|
||||
virtual bool(void) IsRealSpectator;
|
||||
virtual bool(void) IsDead;
|
||||
virtual bool(void) IsPlayer;
|
||||
|
||||
#ifdef SERVER
|
||||
virtual void(void) EvaluateEntity;
|
||||
virtual float(entity, float) SendEntity;
|
||||
virtual void(void) RunClientCommand;
|
||||
#else
|
||||
virtual void(void) ClientInputFrame;
|
||||
virtual void(float,float) ReceiveEntity;
|
||||
virtual float(void) predraw;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CLIENT
|
||||
void Spectator_ReadEntity(float new);
|
||||
#endif
|
483
src/shared/NSClientSpectator.qc
Normal file
483
src/shared/NSClientSpectator.qc
Normal file
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
bool
|
||||
NSClientSpectator::IsRealSpectator(void)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientSpectator::IsDead(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientSpectator::IsPlayer(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool
|
||||
NSClientSpectator::IsFakeSpectator(void)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::ClientInput(void)
|
||||
{
|
||||
if (input_buttons & INPUT_BUTTON0) {
|
||||
InputNext();
|
||||
} else if (input_buttons & INPUT_BUTTON3) {
|
||||
InputPrevious();
|
||||
} else if (input_buttons & INPUT_BUTTON2) {
|
||||
InputMode();
|
||||
} else {
|
||||
spec_flags &= ~SPECFLAG_BUTTON_RELEASED;
|
||||
}
|
||||
|
||||
input_buttons = 0;
|
||||
//crossprint(sprintf("%d %d %d\n", spec_ent, spec_mode, spec_flags));
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::WarpToTarget(void)
|
||||
{
|
||||
entity b = edict_num(spec_ent);
|
||||
|
||||
setorigin(this, b.origin);
|
||||
}
|
||||
|
||||
#ifdef SERVER
|
||||
float
|
||||
NSClientSpectator::SendEntity(entity ePVSent, float flChangedFlags)
|
||||
{
|
||||
if (this != ePVSent) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (clienttype(ePVSent) != CLIENTTYPE_REAL) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
WriteByte(MSG_ENTITY, ENT_SPECTATOR);
|
||||
WriteFloat(MSG_ENTITY, flChangedFlags);
|
||||
|
||||
if (flChangedFlags & SPECFL_ORIGIN) {
|
||||
WriteCoord(MSG_ENTITY, origin[0]);
|
||||
WriteCoord(MSG_ENTITY, origin[1]);
|
||||
WriteCoord(MSG_ENTITY, origin[2]);
|
||||
}
|
||||
|
||||
if (flChangedFlags & SPECFL_VELOCITY) {
|
||||
WriteFloat(MSG_ENTITY, velocity[0]);
|
||||
WriteFloat(MSG_ENTITY, velocity[1]);
|
||||
WriteFloat(MSG_ENTITY, velocity[2]);
|
||||
}
|
||||
|
||||
if (flChangedFlags & SPECFL_TARGET)
|
||||
WriteByte(MSG_ENTITY, spec_ent);
|
||||
|
||||
if (flChangedFlags & SPECFL_MODE)
|
||||
WriteByte(MSG_ENTITY, spec_mode);
|
||||
|
||||
if (flChangedFlags & SPECFL_FLAGS)
|
||||
WriteByte(MSG_ENTITY, spec_flags);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::RunClientCommand(void)
|
||||
{
|
||||
runstandardplayerphysics(this);
|
||||
ClientInput();
|
||||
}
|
||||
|
||||
#else
|
||||
void
|
||||
NSClientSpectator::ClientInputFrame(void)
|
||||
{
|
||||
/* If we are inside a VGUI, don't let the client do stuff outside */
|
||||
if (VGUI_Active()) {
|
||||
input_impulse = 0;
|
||||
input_buttons = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* background maps have no input */
|
||||
if (serverkeyfloat("background") == 1)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::ReceiveEntity(float new, float fl)
|
||||
{
|
||||
if (new == FALSE) {
|
||||
/* Go through all the physics code between the last received frame
|
||||
* and the newest frame and keep the changes this time around instead
|
||||
* of rolling back, because we'll apply the new server-verified values
|
||||
* right after anyway. */
|
||||
/* FIXME: splitscreen */
|
||||
if (entnum == player_localentnum) {
|
||||
/* FIXME: splitscreen */
|
||||
pSeat = &g_seats[0];
|
||||
|
||||
for (int i = sequence+1; i <= servercommandframe; i++) {
|
||||
/* ...maybe the input state is too old? */
|
||||
if (!getinputstate(i)) {
|
||||
break;
|
||||
}
|
||||
input_sequence = i;
|
||||
runstandardplayerphysics(this);
|
||||
ClientInput();
|
||||
}
|
||||
|
||||
/* any differences in things that are read below are now
|
||||
* officially from prediction misses. */
|
||||
}
|
||||
}
|
||||
|
||||
/* seed for our prediction table */
|
||||
sequence = servercommandframe;
|
||||
|
||||
if (fl & SPECFL_ORIGIN) {
|
||||
origin[0] = readcoord();
|
||||
origin[1] = readcoord();
|
||||
origin[2] = readcoord();
|
||||
}
|
||||
|
||||
if (fl & SPECFL_VELOCITY) {
|
||||
velocity[0] = readfloat();
|
||||
velocity[1] = readfloat();
|
||||
velocity[2] = readfloat();
|
||||
}
|
||||
|
||||
if (fl & SPECFL_TARGET)
|
||||
spec_ent = readbyte();
|
||||
|
||||
if (fl & SPECFL_MODE)
|
||||
spec_mode = readbyte();
|
||||
|
||||
if (fl & SPECFL_FLAGS)
|
||||
spec_flags = readbyte();
|
||||
};
|
||||
float
|
||||
NSClientSpectator::predraw(void)
|
||||
{
|
||||
addentity(this);
|
||||
return (PREDRAW_NEXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
NSClientSpectator::InputNext(void)
|
||||
{
|
||||
if (spec_flags & SPECFLAG_BUTTON_RELEASED)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
float max_edict;
|
||||
|
||||
max_edict = serverkeyfloat("sv_playerslots");
|
||||
|
||||
spec_ent++;
|
||||
|
||||
if (spec_ent > max_edict)
|
||||
spec_ent = 1;
|
||||
|
||||
print(sprintf("edict: %d\n", spec_ent));
|
||||
#else
|
||||
float max_edict;
|
||||
float sep = spec_ent;
|
||||
float best = 0;
|
||||
NSClient cl;
|
||||
|
||||
max_edict = serverkeyfloat("sv_playerslots");
|
||||
|
||||
for (float i = 1; i <= max_edict; i++) {
|
||||
entity f;
|
||||
|
||||
if (i <= sep && best == 0) {
|
||||
f = edict_num(i);
|
||||
if (f && f.classname == "player" && f != this) {
|
||||
cl = (NSClient)f;
|
||||
if (!cl.IsFakeSpectator())
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > sep) {
|
||||
f = edict_num(i);
|
||||
if (f && f.classname == "player" && f != this) {
|
||||
cl = (NSClient)f;
|
||||
if (!cl.IsFakeSpectator()) {
|
||||
best = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 0)
|
||||
return;
|
||||
|
||||
spec_ent = best;
|
||||
#endif
|
||||
spec_flags |= SPECFLAG_BUTTON_RELEASED;
|
||||
WarpToTarget();
|
||||
|
||||
if (spec_mode == SPECMODE_FREE)
|
||||
spec_mode = SPECMODE_THIRDPERSON;
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::InputPrevious(void)
|
||||
{
|
||||
if (spec_flags & SPECFLAG_BUTTON_RELEASED)
|
||||
return;
|
||||
#if 0
|
||||
float max_edict;
|
||||
|
||||
max_edict = serverkeyfloat("sv_playerslots");
|
||||
|
||||
spec_ent--;
|
||||
|
||||
if (spec_ent < 1)
|
||||
spec_ent = max_edict;
|
||||
#else
|
||||
float max_edict;
|
||||
float sep = spec_ent;
|
||||
float best = 0;
|
||||
NSClient cl;
|
||||
|
||||
max_edict = serverkeyfloat("sv_playerslots");
|
||||
|
||||
for (float i = max_edict; i > 0; i--) {
|
||||
entity f;
|
||||
|
||||
/* remember the first valid one here */
|
||||
if (i >= sep && best == 0) {
|
||||
f = edict_num(i);
|
||||
|
||||
if (f && f.classname == "player") {
|
||||
cl = (NSClient)f;
|
||||
|
||||
if (!cl.IsFakeSpectator())
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the first good one and take it */
|
||||
if (i < sep) {
|
||||
f = edict_num(i);
|
||||
if (f && f.classname == "player") {
|
||||
cl = (NSClient)f;
|
||||
if (!cl.IsFakeSpectator()) {
|
||||
best = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 0)
|
||||
return;
|
||||
|
||||
spec_ent = best;
|
||||
#endif
|
||||
|
||||
spec_flags |= SPECFLAG_BUTTON_RELEASED;
|
||||
|
||||
WarpToTarget();
|
||||
|
||||
if (spec_mode == SPECMODE_FREE)
|
||||
spec_mode = SPECMODE_THIRDPERSON;
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::InputMode(void)
|
||||
{
|
||||
if (spec_flags & SPECFLAG_BUTTON_RELEASED)
|
||||
return;
|
||||
|
||||
NSClient f;
|
||||
#ifdef CLIENT
|
||||
f = (NSClient)findfloat(world, ::entnum, spec_ent);
|
||||
#else
|
||||
f = (NSClient)edict_num(spec_ent);
|
||||
#endif
|
||||
|
||||
if (f == this || f.classname != "player")
|
||||
spec_mode = SPECMODE_FREE;
|
||||
else {
|
||||
spec_mode++;
|
||||
|
||||
if (spec_mode > SPECMODE_FIRSTPERSON)
|
||||
spec_mode = SPECMODE_FREE;
|
||||
}
|
||||
|
||||
spec_flags |= SPECFLAG_BUTTON_RELEASED;
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::PreFrame(void)
|
||||
{
|
||||
#ifdef CLIENT
|
||||
/* base player attributes/fields we're going to roll back */
|
||||
SAVE_STATE(origin);
|
||||
SAVE_STATE(velocity);
|
||||
SAVE_STATE(spec_ent);
|
||||
SAVE_STATE(spec_mode);
|
||||
SAVE_STATE(spec_flags);
|
||||
|
||||
/* run physics code for all the input frames which we've not heard back
|
||||
* from yet. This continues on in Player_ReceiveEntity! */
|
||||
for (int i = sequence + 1; i <= clientcommandframe; i++) {
|
||||
float flSuccess = getinputstate(i);
|
||||
if (flSuccess == FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i==clientcommandframe){
|
||||
CSQC_Input_Frame();
|
||||
}
|
||||
|
||||
/* don't do partial frames, aka incomplete input packets */
|
||||
if (input_timelength == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* this global is for our shared random number seed */
|
||||
input_sequence = i;
|
||||
|
||||
/* run our custom physics */
|
||||
runstandardplayerphysics(this);
|
||||
ClientInput();
|
||||
}
|
||||
#endif
|
||||
|
||||
SpectatorTrackPlayer();
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::SpectatorTrackPlayer(void)
|
||||
{
|
||||
if (spec_mode == SPECMODE_THIRDPERSON || spec_mode == SPECMODE_FIRSTPERSON ) {
|
||||
NSClient b;
|
||||
|
||||
#ifdef CLIENT
|
||||
b = (NSClient)findfloat(world, ::entnum, spec_ent);
|
||||
#else
|
||||
b = (NSClient)edict_num(spec_ent);
|
||||
#endif
|
||||
|
||||
if (b && b.classname == "player")
|
||||
if (b.IsFakeSpectator()) {
|
||||
b = 0;
|
||||
spec_mode = SPECMODE_FREE;
|
||||
InputNext();
|
||||
}
|
||||
|
||||
/* if the ent is dead... or not available in this current frame
|
||||
just warp to the last 'good' one */
|
||||
if (b) {
|
||||
setorigin(this, b.origin);
|
||||
spec_org = b.origin;
|
||||
} else {
|
||||
setorigin(this, spec_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SERVER
|
||||
void
|
||||
NSClientSpectator::EvaluateEntity(void)
|
||||
{
|
||||
/* check for which values have changed in this frame
|
||||
and announce to network said changes */
|
||||
if (origin != origin_net)
|
||||
SetSendFlags(SPECFL_ORIGIN);
|
||||
|
||||
if (velocity != velocity_net)
|
||||
SetSendFlags(SPECFL_VELOCITY);
|
||||
|
||||
if (spec_ent != spec_ent_net)
|
||||
SetSendFlags(SPECFL_TARGET);
|
||||
|
||||
if (spec_mode != spec_mode_net)
|
||||
SetSendFlags(SPECFL_MODE);
|
||||
|
||||
if (spec_flags != spec_flags_net)
|
||||
SetSendFlags(SPECFL_FLAGS);
|
||||
|
||||
SAVE_STATE(origin);
|
||||
SAVE_STATE(velocity);
|
||||
SAVE_STATE(spec_ent);
|
||||
SAVE_STATE(spec_mode);
|
||||
SAVE_STATE(spec_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
NSClientSpectator::PostFrame(void)
|
||||
{
|
||||
#ifdef CLIENT
|
||||
ROLL_BACK(origin);
|
||||
ROLL_BACK(velocity);
|
||||
ROLL_BACK(spec_ent);
|
||||
ROLL_BACK(spec_mode);
|
||||
ROLL_BACK(spec_flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NSClientSpectator::NSClientSpectator(void)
|
||||
{
|
||||
super::NSClient();
|
||||
modelindex = 0;
|
||||
flags = FL_CLIENT;
|
||||
SetSolid(SOLID_NOT);
|
||||
SetMovetype(MOVETYPE_NOCLIP);
|
||||
think = __NULL__;
|
||||
nextthink = 0.0f;
|
||||
maxspeed = 250;
|
||||
spec_ent = 0;
|
||||
spec_mode = 0;
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
void
|
||||
Spectator_ReadEntity(float new)
|
||||
{
|
||||
NSClientSpectator spec = (NSClientSpectator)self;
|
||||
|
||||
if (new || self.classname != "spectator") {
|
||||
spawnfunc_NSClientSpectator();
|
||||
spec.classname = "spectator";
|
||||
spec.solid = SOLID_NOT;
|
||||
spec.drawmask = MASK_ENGINE;
|
||||
spec.customphysics = Empty;
|
||||
setsize(spec, [0,0,0], [0,0,0]);
|
||||
}
|
||||
|
||||
float flags = readfloat();
|
||||
spec.ReceiveEntity(new, flags);
|
||||
}
|
||||
#endif
|
Loading…
Reference in a new issue