nuclide/src/shared/NSClientSpectator.qc

491 lines
9.3 KiB
Plaintext

/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* 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.
*/
void
NSClientSpectator::NSClientSpectator(void)
{
flags |= FL_CLIENT;
}
#ifdef SERVER
void
NSClientSpectator::Save(float handle)
{
super::Save(handle);
}
void
NSClientSpectator::Restore(string strKey, string strValue)
{
switch (strKey) {
default:
super::Restore(strKey, strValue);
}
}
#endif
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
}
#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