Basic spectator implementation for all games.
This commit is contained in:
parent
f918e9ddb9
commit
95739c7a20
13 changed files with 642 additions and 64 deletions
|
@ -33,6 +33,7 @@ Bot_AddQuick(void)
|
|||
{
|
||||
/* we've got no nodes, so generate some fake waypoints */
|
||||
if (!g_nodes_present) {
|
||||
print("^1Bot_AddQuick^7: Can't add bot. No waypoints.\n");
|
||||
return world;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ Chat_Draw(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
drawfont = FONT_CON;
|
||||
|
||||
/* the voting stuff resides here too, for now */
|
||||
if (serverkey("vote_cmd")) {
|
||||
string tempstr;
|
||||
|
|
|
@ -62,6 +62,7 @@ var int MUZZLE_RIFLE;
|
|||
var int MUZZLE_WEIRD;
|
||||
|
||||
var int SHELL_DEFAULT;
|
||||
var int SHELL_SHOTGUN;
|
||||
|
||||
/* misc globals */
|
||||
vector video_mins;
|
||||
|
|
|
@ -140,6 +140,7 @@ void
|
|||
CSQC_UpdateView(float w, float h, float focus)
|
||||
{
|
||||
player pl;
|
||||
spectator spec;
|
||||
int s;
|
||||
|
||||
if (w == 0 || h == 0) {
|
||||
|
@ -210,50 +211,58 @@ CSQC_UpdateView(float w, float h, float focus)
|
|||
continue;
|
||||
}
|
||||
|
||||
pl = (player)self;
|
||||
if (self.classname == "player") {
|
||||
pl = (player)self;
|
||||
Predict_PlayerPreFrame(pl);
|
||||
|
||||
Predict_PreFrame((player)self);
|
||||
pSeat->m_vecPredictedOrigin = pl.origin;
|
||||
pSeat->m_vecPredictedVelocity = pl.velocity;
|
||||
pSeat->m_flPredictedFlags = pl.flags;
|
||||
|
||||
pSeat->m_vecPredictedOrigin = pl.origin;
|
||||
pSeat->m_vecPredictedVelocity = pl.velocity;
|
||||
pSeat->m_flPredictedFlags = pl.flags;
|
||||
|
||||
/* Don't hide the player entity */
|
||||
if (autocvar_cl_thirdperson == TRUE && pl.health) {
|
||||
setproperty(VF_VIEWENTITY, (float)0);
|
||||
} else {
|
||||
setproperty(VF_VIEWENTITY, (float)player_localentnum);
|
||||
}
|
||||
|
||||
float oldzoom = pl.viewzoom;
|
||||
if (pl.viewzoom == 1.0f) {
|
||||
pl.viewzoom = 1.0 - (0.5 * pSeat->m_flZoomTime);
|
||||
|
||||
/* +zoomin requested by Slacer */
|
||||
if (pSeat->m_iZoomed) {
|
||||
pSeat->m_flZoomTime += clframetime * 15;
|
||||
/* Don't hide the player entity */
|
||||
if (autocvar_cl_thirdperson == TRUE && pl.health) {
|
||||
setproperty(VF_VIEWENTITY, (float)0);
|
||||
} else {
|
||||
pSeat->m_flZoomTime -= clframetime * 15;
|
||||
setproperty(VF_VIEWENTITY, (float)player_localentnum);
|
||||
}
|
||||
pSeat->m_flZoomTime = bound(0, pSeat->m_flZoomTime, 1);
|
||||
|
||||
float oldzoom = pl.viewzoom;
|
||||
if (pl.viewzoom == 1.0f) {
|
||||
pl.viewzoom = 1.0 - (0.5 * pSeat->m_flZoomTime);
|
||||
|
||||
/* +zoomin requested by Slacer */
|
||||
if (pSeat->m_iZoomed) {
|
||||
pSeat->m_flZoomTime += clframetime * 15;
|
||||
} else {
|
||||
pSeat->m_flZoomTime -= clframetime * 15;
|
||||
}
|
||||
pSeat->m_flZoomTime = bound(0, pSeat->m_flZoomTime, 1);
|
||||
}
|
||||
|
||||
setproperty(VF_AFOV, cvar("fov") * pl.viewzoom);
|
||||
|
||||
if (autocvar_zoom_sensitivity && pl.viewzoom < 1.0f) {
|
||||
setsensitivityscaler(pl.viewzoom * autocvar_zoom_sensitivity);
|
||||
} else {
|
||||
setsensitivityscaler(pl.viewzoom);
|
||||
}
|
||||
|
||||
if (pl.viewzoom <= 0.0f) {
|
||||
setsensitivityscaler(1.0f);
|
||||
}
|
||||
|
||||
pl.viewzoom = oldzoom;
|
||||
|
||||
View_PreDraw();
|
||||
} else if (self.classname == "spectator") {
|
||||
spec = (spectator)self;
|
||||
Predict_SpectatorPreFrame(spec);
|
||||
|
||||
pSeat->m_vecPredictedOrigin = spec.origin;
|
||||
pSeat->m_vecPredictedVelocity = spec.velocity;
|
||||
pSeat->m_flPredictedFlags = spec.flags;
|
||||
}
|
||||
|
||||
setproperty(VF_AFOV, cvar("fov") * pl.viewzoom);
|
||||
|
||||
if (autocvar_zoom_sensitivity && pl.viewzoom < 1.0f) {
|
||||
setsensitivityscaler(pl.viewzoom * autocvar_zoom_sensitivity);
|
||||
} else {
|
||||
setsensitivityscaler(pl.viewzoom);
|
||||
}
|
||||
|
||||
if (pl.viewzoom <= 0.0f) {
|
||||
setsensitivityscaler(1.0f);
|
||||
}
|
||||
|
||||
pl.viewzoom = oldzoom;
|
||||
|
||||
View_PreDraw();
|
||||
|
||||
if (pSeat->m_pWeaponFX) {
|
||||
CBaseFX p = (CBaseFX)pSeat->m_pWeaponFX;
|
||||
p.Draw();
|
||||
|
@ -267,18 +276,58 @@ CSQC_UpdateView(float w, float h, float focus)
|
|||
setproperty(VF_CL_VIEWANGLES, view_angles);
|
||||
setproperty(VF_ANGLES, view_angles);
|
||||
} else {
|
||||
if (pl.health) {
|
||||
if (autocvar_cl_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, self);
|
||||
setproperty(VF_ORIGIN, trace_endpos + (v_forward * 5));
|
||||
if (self.classname == "player") {
|
||||
if (pl.health) {
|
||||
if (autocvar_cl_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, self);
|
||||
setproperty(VF_ORIGIN, trace_endpos + (v_forward * 5));
|
||||
} else {
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs);
|
||||
}
|
||||
} else {
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + pl.view_ofs);
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
||||
}
|
||||
|
||||
if (pSeat->m_flShakeDuration > 0.0) {
|
||||
vector vecShake = [0,0,0];
|
||||
vecShake[0] += random() * 3;
|
||||
vecShake[1] += random() * 3;
|
||||
vecShake[2] += random() * 3;
|
||||
pl.punchangle += (vecShake * pSeat->m_flShakeAmp) * (pSeat->m_flShakeDuration / pSeat->m_flShakeTime);
|
||||
pSeat->m_flShakeDuration -= clframetime;
|
||||
}
|
||||
setproperty(VF_ANGLES, view_angles + pl.punchangle);
|
||||
} else if (self.classname == "spectator") {
|
||||
switch (spec.spec_mode) {
|
||||
case SPECMODE_THIRDPERSON:
|
||||
makevectors(view_angles);
|
||||
vector vecStart;
|
||||
vecStart[0] = pSeat->m_vecPredictedOrigin[0];
|
||||
vecStart[1] = pSeat->m_vecPredictedOrigin[1];
|
||||
vecStart[2] = pSeat->m_vecPredictedOrigin[2] + 16;
|
||||
vecStart += (v_right * 4);
|
||||
|
||||
vector vecEnd = vecStart + (v_forward * -48) + [0,0,16] + (v_right * 4);
|
||||
traceline(vecStart, vecEnd, FALSE, self);
|
||||
setproperty(VF_ORIGIN, trace_endpos + (v_forward * 5));
|
||||
break;
|
||||
case SPECMODE_FIRSTPERSON:
|
||||
entity b;
|
||||
b = findfloat(world, ::entnum, spec.spec_ent);
|
||||
|
||||
if (b.classname == "player") {
|
||||
player bp = (player)b;
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin + bp.view_ofs);
|
||||
setproperty(VF_ANGLES, bp.v_angle);
|
||||
setproperty(VF_CL_VIEWANGLES, [bp.pitch, bp.angles[1], bp.angles[2]]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
||||
}
|
||||
} else {
|
||||
setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin);
|
||||
}
|
||||
|
||||
if (g_iIntermission) {
|
||||
|
@ -287,16 +336,6 @@ CSQC_UpdateView(float w, float h, float focus)
|
|||
setproperty(VF_ORIGIN, pSeat->m_vecCameraOrigin);
|
||||
setproperty(VF_CL_VIEWANGLES, view_angles);
|
||||
}
|
||||
|
||||
if (pSeat->m_flShakeDuration > 0.0) {
|
||||
vector vecShake = [0,0,0];
|
||||
vecShake[0] += random() * 3;
|
||||
vecShake[1] += random() * 3;
|
||||
vecShake[2] += random() * 3;
|
||||
pl.punchangle += (vecShake * pSeat->m_flShakeAmp) * (pSeat->m_flShakeDuration / pSeat->m_flShakeTime);
|
||||
pSeat->m_flShakeDuration -= clframetime;
|
||||
}
|
||||
setproperty(VF_ANGLES, view_angles + pl.punchangle);
|
||||
}
|
||||
|
||||
setproperty(VF_DRAWWORLD, 1);
|
||||
|
@ -335,9 +374,9 @@ CSQC_UpdateView(float w, float h, float focus)
|
|||
GameText_Draw();
|
||||
PointMessage_Draw();
|
||||
|
||||
if (getplayerkeyvalue(player_localnum, "*spec") != "0") {
|
||||
if (self.classname == "spectator") {
|
||||
HUD_DrawSpectator();
|
||||
} else {
|
||||
} else if (self.classname == "player") {
|
||||
HUD_Draw();
|
||||
}
|
||||
|
||||
|
@ -354,7 +393,10 @@ CSQC_UpdateView(float w, float h, float focus)
|
|||
}
|
||||
}
|
||||
|
||||
Predict_PostFrame((player)self);
|
||||
if (self.classname == "player")
|
||||
Predict_PlayerPostFrame((player)self);
|
||||
else if (self.classname == "spectator")
|
||||
Predict_SpectatorPostFrame((spectator)self);
|
||||
}
|
||||
|
||||
DSP_UpdateListener();
|
||||
|
@ -588,10 +630,13 @@ CSQC_Parse_Event(void)
|
|||
setproperty(VF_ANGLES, a);
|
||||
break;
|
||||
case EV_SHAKE:
|
||||
if (self.classname == "spectator")
|
||||
break;
|
||||
pSeat->m_flShakeDuration = readfloat();
|
||||
pSeat->m_flShakeAmp = readfloat();
|
||||
pSeat->m_flShakeFreq = readfloat();
|
||||
pSeat->m_flShakeTime = pSeat->m_flShakeDuration;
|
||||
break;
|
||||
default:
|
||||
ClientGame_EventParse(fHeader);
|
||||
}
|
||||
|
@ -833,7 +878,7 @@ CSQC_Ent_Update(float new)
|
|||
break;
|
||||
case ENT_PLAYER:
|
||||
player pl = (player)self;
|
||||
if (new) {
|
||||
if (new || self.classname != "player") {
|
||||
spawnfunc_player();
|
||||
pl.classname = "player";
|
||||
pl.solid = SOLID_SLIDEBOX;
|
||||
|
@ -843,6 +888,18 @@ CSQC_Ent_Update(float new)
|
|||
}
|
||||
pl.ReceiveEntity(new);
|
||||
break;
|
||||
case ENT_SPECTATOR:
|
||||
spectator spec = (spectator)self;
|
||||
if (new || self.classname != "spectator") {
|
||||
spawnfunc_spectator();
|
||||
spec.classname = "spectator";
|
||||
spec.solid = SOLID_SLIDEBOX;
|
||||
spec.drawmask = MASK_ENGINE;
|
||||
spec.customphysics = Empty;
|
||||
setsize(spec, [0,0,0], [0,0,0]);
|
||||
}
|
||||
spec.ReceiveEntity(new);
|
||||
break;
|
||||
case ENT_SPRITE:
|
||||
env_sprite spr = (env_sprite)self;
|
||||
if (new) {
|
||||
|
|
|
@ -24,7 +24,7 @@ Propagate our pmove state to whatever the current frame before its stomped on
|
|||
=================
|
||||
*/
|
||||
void
|
||||
Predict_PreFrame(player pl)
|
||||
Predict_PlayerPreFrame(player pl)
|
||||
{
|
||||
/* base player attributes/fields we're going to roll back */
|
||||
pl.net_origin = pl.origin;
|
||||
|
@ -78,7 +78,7 @@ Rewind our pmove state back to before we started predicting.
|
|||
=================
|
||||
*/
|
||||
void
|
||||
Predict_PostFrame(player pl)
|
||||
Predict_PlayerPostFrame(player pl)
|
||||
{
|
||||
/* finally roll the values back */
|
||||
pl.origin = pl.net_origin;
|
||||
|
@ -99,3 +99,33 @@ Predict_PostFrame(player pl)
|
|||
/* update bounds */
|
||||
setorigin(pl, pl.origin);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Predict_PreFrame
|
||||
|
||||
We're part way through parsing new player data.
|
||||
Propagate our pmove state to whatever the current frame before its stomped on
|
||||
(so any non-networked state updates locally).
|
||||
=================
|
||||
*/
|
||||
void
|
||||
Predict_SpectatorPreFrame(spectator pl)
|
||||
{
|
||||
pl.PreFrame();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Predict_SpectatorPostFrame
|
||||
|
||||
We're part way through parsing new player data.
|
||||
Rewind our pmove state back to before we started predicting.
|
||||
(to give consistent state instead of accumulating errors)
|
||||
=================
|
||||
*/
|
||||
void
|
||||
Predict_SpectatorPostFrame(spectator pl)
|
||||
{
|
||||
pl.PostFrame();
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ View_Init(void)
|
|||
MUZZLE_SMALL = (int)getmodelindex("sprites/muzzleflash2.spr");
|
||||
MUZZLE_WEIRD = (int)getmodelindex("sprites/muzzleflash3.spr");
|
||||
SHELL_DEFAULT = (int)getmodelindex("models/shell.mdl");
|
||||
SHELL_SHOTGUN = (int)getmodelindex("models/shotgunshell.mdl");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -123,6 +123,13 @@ void
|
|||
SpectatorThink(void)
|
||||
{
|
||||
Game_SpectatorThink();
|
||||
|
||||
if (self.classname == "spectator") {
|
||||
spectator spec = (spectator)self;
|
||||
spec.PreFrame();
|
||||
spec.PostFrame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -137,6 +144,7 @@ void
|
|||
SpectatorConnect(void)
|
||||
{
|
||||
Game_SpectatorConnect();
|
||||
spawnfunc_spectator();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -194,6 +202,12 @@ times the amount of players in a given game.
|
|||
void
|
||||
PlayerPreThink(void)
|
||||
{
|
||||
if (self.classname == "spectator") {
|
||||
//spectator spec = (spectator)self;
|
||||
//spec.PreFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
@ -220,6 +234,11 @@ times the amount of players in a given game.
|
|||
void
|
||||
PlayerPostThink(void)
|
||||
{
|
||||
if (self.classname == "spectator") {
|
||||
SpectatorThink();
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
@ -289,6 +308,11 @@ with the input_X globals being set to the appropriate data.
|
|||
void
|
||||
SV_RunClientCommand(void)
|
||||
{
|
||||
if (self.classname == "spectator") {
|
||||
spectator spec = (spectator)self;
|
||||
spec.RunClientCommand();
|
||||
}
|
||||
|
||||
if (self.classname != "player") {
|
||||
return;
|
||||
}
|
||||
|
@ -325,6 +349,23 @@ SV_ParseClientCommand(string cmd)
|
|||
Game_ParseClientCommand(cmd);
|
||||
else
|
||||
Game_ParseClientCommand(newcmd);
|
||||
|
||||
tokenize(cmd);
|
||||
|
||||
switch (argv(0)) {
|
||||
case "spectate":
|
||||
if (self.classname != "player")
|
||||
break;
|
||||
ClientKill();
|
||||
spawnfunc_spectator();
|
||||
break;
|
||||
case "play":
|
||||
if (self.classname != "spectator")
|
||||
break;
|
||||
spawnfunc_player();
|
||||
PutClientInServer();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -542,6 +583,16 @@ ConsoleCmd(string cmd)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!self) {
|
||||
for ( other = world; ( other = find( other, classname, "spectator" ) ); ) {
|
||||
if ( clienttype( other ) == CLIENTTYPE_REAL ) {
|
||||
self = other;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pl = (player)self;
|
||||
|
||||
/* give the game-mode a chance to override us */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "sound.h"
|
||||
#include "pmove.h"
|
||||
#include "memory.h"
|
||||
#include "spectator.h"
|
||||
|
||||
#define BSPVER_PREREL 28
|
||||
#define BSPVER_Q1 29
|
||||
|
|
|
@ -20,6 +20,7 @@ enum
|
|||
ENT_NONE,
|
||||
ENT_ENTITY,
|
||||
ENT_PLAYER,
|
||||
ENT_SPECTATOR,
|
||||
ENT_AMBIENTSOUND,
|
||||
ENT_DLIGHT,
|
||||
ENT_PROJECTEDTEXTURE,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#includelist
|
||||
spectator.qc
|
||||
pmove.qc
|
||||
sound.qc
|
||||
math.qc
|
||||
|
|
|
@ -30,6 +30,8 @@ class base_player
|
|||
vector view_ofs;
|
||||
float weapontime;
|
||||
|
||||
vector v_angle;
|
||||
|
||||
/* any mods that use hooks */
|
||||
entity hook;
|
||||
|
||||
|
|
52
src/shared/spectator.h
Normal file
52
src/shared/spectator.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
enumflags
|
||||
{
|
||||
SPECFL_ORIGIN,
|
||||
SPECFL_VELOCITY,
|
||||
SPECFL_TARGET,
|
||||
SPECFL_MODE,
|
||||
SPECFL_FLAGS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SPECMODE_FREE,
|
||||
SPECMODE_THIRDPERSON,
|
||||
SPECMODE_FIRSTPERSON,
|
||||
SPECMODE_OVERVIEW
|
||||
};
|
||||
|
||||
#ifdef SERVER
|
||||
class spectator:CBaseEntity
|
||||
#else
|
||||
class spectator
|
||||
#endif
|
||||
{
|
||||
vector origin_net;
|
||||
vector velocity_net;
|
||||
float spec_ent; float spec_ent_net;
|
||||
float spec_mode; float spec_mode_net;
|
||||
float spec_flags; float spec_flags_net;
|
||||
|
||||
int sequence;
|
||||
|
||||
void(void) spectator;
|
||||
|
||||
virtual void(void) playernext;
|
||||
virtual void(void) playerprevious;
|
||||
virtual void(void) modeswitch;
|
||||
|
||||
virtual void(void) PreFrame;
|
||||
virtual void(void) PostFrame;
|
||||
|
||||
virtual void(void) Input;
|
||||
|
||||
virtual void(void) WarpToTarget;
|
||||
|
||||
#ifdef SERVER
|
||||
virtual float(entity, float) SendEntity;
|
||||
virtual void(void) RunClientCommand;
|
||||
#else
|
||||
virtual void(float) ReceiveEntity;
|
||||
virtual float(void) predraw;
|
||||
#endif
|
||||
};
|
378
src/shared/spectator.qc
Normal file
378
src/shared/spectator.qc
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2021 Marco Hladik <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.
|
||||
*/
|
||||
|
||||
void
|
||||
spectator::WarpToTarget(void)
|
||||
{
|
||||
entity b = edict_num(spec_ent);
|
||||
|
||||
setorigin(this, b.origin);
|
||||
}
|
||||
|
||||
void
|
||||
spectator::Input(void)
|
||||
{
|
||||
if (input_buttons & INPUT_BUTTON0) {
|
||||
playernext();
|
||||
} else if (input_buttons & INPUT_BUTTON3) {
|
||||
playerprevious();
|
||||
} else if (input_buttons & INPUT_BUTTON2) {
|
||||
if (spec_flags & GF_SEMI_TOGGLED)
|
||||
return;
|
||||
spec_mode++;
|
||||
|
||||
if (spec_mode > SPECMODE_FIRSTPERSON)
|
||||
spec_mode = SPECMODE_FREE;
|
||||
|
||||
spec_flags |= GF_SEMI_TOGGLED;
|
||||
} else {
|
||||
spec_flags &= ~GF_SEMI_TOGGLED;
|
||||
}
|
||||
|
||||
input_buttons = 0;
|
||||
}
|
||||
|
||||
#ifdef SERVER
|
||||
float
|
||||
spectator::SendEntity(entity ePVSent, float flChangedFlags)
|
||||
{
|
||||
if (this != ePVSent) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (clienttype(ePVSent) != CLIENTTYPE_REAL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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 TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
spectator::RunClientCommand(void)
|
||||
{
|
||||
runstandardplayerphysics(this);
|
||||
Input();
|
||||
}
|
||||
|
||||
#else
|
||||
void
|
||||
spectator::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);
|
||||
Input();
|
||||
}
|
||||
|
||||
/* any differences in things that are read below are now
|
||||
* officially from prediction misses. */
|
||||
}
|
||||
}
|
||||
|
||||
/* seed for our prediction table */
|
||||
sequence = servercommandframe;
|
||||
|
||||
fl = readfloat();
|
||||
|
||||
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
|
||||
spectator::predraw(void)
|
||||
{
|
||||
addentity(this);
|
||||
return PREDRAW_NEXT;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
spectator::playernext(void)
|
||||
{
|
||||
if (spec_flags & GF_SEMI_TOGGLED)
|
||||
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;
|
||||
|
||||
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") {
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > sep) {
|
||||
f = edict_num(i);
|
||||
if (f && f.classname == "player") {
|
||||
best = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 0)
|
||||
return;
|
||||
|
||||
spec_ent = best;
|
||||
#endif
|
||||
spec_flags |= GF_SEMI_TOGGLED;
|
||||
WarpToTarget();
|
||||
|
||||
if (spec_mode == SPECMODE_FREE)
|
||||
spec_mode = SPECMODE_THIRDPERSON;
|
||||
}
|
||||
|
||||
void
|
||||
spectator::playerprevious(void)
|
||||
{
|
||||
if (spec_flags & GF_SEMI_TOGGLED)
|
||||
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;
|
||||
|
||||
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") {
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the first good one and take it */
|
||||
if (i < sep) {
|
||||
f = edict_num(i);
|
||||
if (f && f.classname == "player") {
|
||||
best = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == 0)
|
||||
return;
|
||||
|
||||
spec_ent = best;
|
||||
#endif
|
||||
|
||||
print(sprintf("dddd: %d\n", spec_ent));
|
||||
spec_flags |= GF_SEMI_TOGGLED;
|
||||
|
||||
WarpToTarget();
|
||||
|
||||
if (spec_mode == SPECMODE_FREE)
|
||||
spec_mode = SPECMODE_THIRDPERSON;
|
||||
}
|
||||
|
||||
void
|
||||
spectator::modeswitch(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
spectator::PreFrame(void)
|
||||
{
|
||||
#ifdef SERVER
|
||||
|
||||
#else
|
||||
/* base player attributes/fields we're going to roll back */
|
||||
origin_net = origin;
|
||||
velocity_net = velocity;
|
||||
spec_ent_net = spec_ent;
|
||||
spec_mode_net = spec_mode;
|
||||
spec_flags_net = 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);
|
||||
Input();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (spec_mode == SPECMODE_THIRDPERSON || spec_mode == SPECMODE_FIRSTPERSON ) {
|
||||
entity b;
|
||||
|
||||
#ifdef CLIENT
|
||||
b = findfloat(world, ::entnum, spec_ent);
|
||||
#else
|
||||
b = edict_num(spec_ent);
|
||||
#endif
|
||||
setorigin(this, b.origin);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
spectator::PostFrame(void)
|
||||
{
|
||||
#ifdef SERVER
|
||||
/* check for which values have changed in this frame
|
||||
and announce to network said changes */
|
||||
if (origin != origin_net)
|
||||
SendFlags |= SPECFL_ORIGIN;
|
||||
|
||||
if (velocity != velocity_net)
|
||||
SendFlags |= SPECFL_VELOCITY;
|
||||
|
||||
if (spec_ent != spec_ent_net)
|
||||
SendFlags |= SPECFL_TARGET;
|
||||
|
||||
if (spec_mode != spec_mode_net)
|
||||
SendFlags |= SPECFL_MODE;
|
||||
|
||||
if (spec_flags != spec_flags_net)
|
||||
SendFlags |= SPECFL_FLAGS;
|
||||
|
||||
origin_net = origin;
|
||||
velocity_net = velocity;
|
||||
spec_ent_net = spec_ent;
|
||||
spec_mode_net = spec_mode;
|
||||
spec_flags_net = spec_flags;
|
||||
#else
|
||||
/* finally roll the values back */
|
||||
origin = origin_net;
|
||||
velocity = velocity_net;
|
||||
spec_ent = spec_ent_net;
|
||||
spec_mode = spec_mode_net;
|
||||
spec_flags = spec_flags_net;
|
||||
setorigin(this, origin);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
spectator::spectator(void)
|
||||
{
|
||||
modelindex = 0;
|
||||
flags = 0;
|
||||
solid = SOLID_NOT;
|
||||
movetype = MOVETYPE_NOCLIP;
|
||||
think = __NULL__;
|
||||
nextthink = 0.0f;
|
||||
maxspeed = 250;
|
||||
|
||||
#ifdef SERVER
|
||||
forceinfokey(this, "*spec", "1");
|
||||
#endif
|
||||
}
|
Loading…
Reference in a new issue