quakeforge/nq/source/cl_main.c
Bill Currie 3eb859a88f Move the entity transform setup into the clients.
This has several benifits:
  o The silly issue with alias model pitches being backwards is kept out
    of the renderer (it's a quakec thing: entites do their pitch
    backwards, but originally, only alias models were rotated. Hipnotic
    did brush entity rotations in the correct direction).
  o Angle to frame vector conversions are done only when the entity's
    angles vector changes, rather than every frame. This avoids a lot of
    unnecessary trig function calls.
  o Once transformed, an entity's frame vectors are always available.
    However, the vectors are left handed rather than right handed (ie,
    forward/left/up instead of forward/right/up): just a matter of
    watching the sign. This avoids even more trig calls (flag models in
    qw).
  o This paves the way for merging brush entity surface rendering with the
    world model surface rendering (the actual goal of this patch).
  o This also paves the way for using quaternions to represent entity
    orientation, as that would be a protocol change.
2011-12-15 17:09:01 +09:00

470 lines
11 KiB
C

/*
cl_main.c
entity parsing and management
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] =
"$Id$";
#include "QF/cbuf.h"
#include "QF/cmd.h"
#include "QF/console.h"
#include "QF/cvar.h"
#include "QF/input.h"
#include "QF/keys.h"
#include "QF/msg.h"
#include "QF/plugin.h"
#include "QF/qfplist.h"
#include "QF/render.h"
#include "QF/screen.h"
#include "QF/skin.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "chase.h"
#include "client.h"
#include "compat.h"
#include "host.h"
#include "host.h"
#include "r_cvar.h"
#include "r_dynamic.h"
#include "server.h"
byte *vid_colormap;
// these two are not intended to be set directly
cvar_t *cl_name;
cvar_t *cl_writecfg;
cvar_t *cl_shownet;
cvar_t *cl_nolerp;
cvar_t *cl_cshift_bonus;
cvar_t *cl_cshift_contents;
cvar_t *cl_cshift_damage;
cvar_t *cl_cshift_powerup;
cvar_t *lookspring;
cvar_t *m_pitch;
cvar_t *m_yaw;
cvar_t *m_forward;
cvar_t *m_side;
cvar_t *hud_fps;
cvar_t *hud_time;
int fps_count;
client_static_t cls;
client_state_t cl;
void
CL_InitCvars (void)
{
cl_cshift_bonus = Cvar_Get ("cl_cshift_bonus", "1", CVAR_ARCHIVE, NULL,
"Show bonus flash on item pickup");
cl_cshift_contents = Cvar_Get ("cl_cshift_content", "1", CVAR_ARCHIVE,
NULL, "Shift view colors for contents "
"(water, slime, etc)");
cl_cshift_damage = Cvar_Get ("cl_cshift_damage", "1", CVAR_ARCHIVE, NULL,
"Shift view colors on damage");
cl_cshift_powerup = Cvar_Get ("cl_cshift_powerup", "1", CVAR_ARCHIVE, NULL, "Shift view colors for powerups");
cl_name = Cvar_Get ("_cl_name", "player", CVAR_ARCHIVE, NULL,
"Player name");
cl_color = Cvar_Get ("_cl_color", "0", CVAR_ARCHIVE, NULL, "Player color");
cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_NONE, NULL,
"turn `run' speed multiplier");
cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE, NULL,
"backward speed");
cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE, NULL,
"forward speed");
cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_NONE, NULL,
"move `run' speed multiplier");
cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_NONE, NULL,
"look up/down speed");
cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_NONE, NULL,
"strafe speed");
cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_NONE, NULL,
"swim/fly up/down speed");
cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_NONE, NULL,
"turning speed");
cl_writecfg = Cvar_Get ("cl_writecfg", "1", CVAR_NONE, NULL,
"write config files?");
cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_NONE, NULL,
"show network packets. 0=off, 1=basic, 2=verbose");
cl_nolerp = Cvar_Get ("cl_nolerp", "0", CVAR_NONE, NULL,
"linear motion interpolation");
lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE, NULL, "Snap view "
"to center when moving and no mlook/klook");
m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE, NULL,
"mouse pitch (up/down) multipier");
m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE, NULL,
"mouse yaw (left/right) multipiler");
m_forward = Cvar_Get ("m_forward", "1", CVAR_ARCHIVE, NULL,
"mouse forward/back speed");
m_side = Cvar_Get ("m_side", "0.8", CVAR_ARCHIVE, NULL,
"mouse strafe speed");
hud_fps = Cvar_Get ("hud_fps", "0", CVAR_ARCHIVE, NULL,
"display realtime frames per second");
Cvar_MakeAlias ("show_fps", hud_fps);
hud_time = Cvar_Get ("hud_time", "0", CVAR_ARCHIVE, NULL,
"display the current time");
}
void
CL_ClearState (void)
{
if (!sv.active)
Host_ClearMemory ();
if (cl.edicts)
PL_Free (cl.edicts);
// wipe the entire cl structure
memset (&cl, 0, sizeof (cl));
cl.chase = 1;
r_force_fullscreen = 0;
CL_Init_Entity (&cl.viewent);
r_view_model = &cl.viewent;
SZ_Clear (&cls.message);
Skin_ClearTempSkins ();
CL_ClearTEnts ();
R_ClearEfrags ();
R_ClearDlights ();
R_ClearParticles ();
CL_ClearEnts ();
}
/*
CL_StopCshifts
Cleans the Cshifts, so your screen doesn't stay red after a timedemo :)
*/
static void
CL_StopCshifts (void)
{
int i;
for (i = 0; i < NUM_CSHIFTS; i++)
cl.cshifts[i].percent = 0;
for (i = 0; i < MAX_CL_STATS; i++)
cl.stats[i] = 0;
}
/*
CL_Disconnect
Sends a disconnect message to the server
This is also called on Host_Error, so it shouldn't cause any errors
*/
void
CL_Disconnect (void)
{
// stop sounds (especially looping!)
S_StopAllSounds ();
// Clean the Cshifts
CL_StopCshifts ();
// bring the console down and fade the colors back to normal
// SCR_BringDownConsole ();
// if running a local server, shut it down
if (cls.demoplayback)
CL_StopPlayback ();
else if (cls.state == ca_connected) {
if (cls.demorecording)
CL_StopRecording ();
Sys_MaskPrintf (SYS_DEV, "Sending clc_disconnect\n");
SZ_Clear (&cls.message);
MSG_WriteByte (&cls.message, clc_disconnect);
NET_SendUnreliableMessage (cls.netcon, &cls.message);
SZ_Clear (&cls.message);
NET_Close (cls.netcon);
CL_SetState (ca_disconnected);
if (sv.active)
Host_ShutdownServer (false);
}
cls.signon = 0;
cl.worldmodel = NULL;
}
void
CL_Disconnect_f (void)
{
CL_Disconnect ();
if (sv.active)
Host_ShutdownServer (false);
}
/*
CL_EstablishConnection
Host should be either "local" or a net address to be passed on
*/
void
CL_EstablishConnection (const char *host)
{
if (cls.state == ca_dedicated)
return;
if (cls.demoplayback)
return;
CL_Disconnect ();
cls.netcon = NET_Connect (host);
if (!cls.netcon)
Host_Error ("CL_Connect: connect failed\n");
Sys_MaskPrintf (SYS_DEV, "CL_EstablishConnection: connected to %s\n",
host);
cls.demonum = -1; // not in the demo loop now
CL_SetState (ca_connected);
cls.signon = 0; // need all the signon messages
// before playing
Key_SetKeyDest (key_game);
}
/*
CL_SignonReply
An svc_signonnum has been received, perform a client side setup
*/
void
CL_SignonReply (void)
{
char str[8192];
Sys_MaskPrintf (SYS_DEV, "CL_SignonReply: %i\n", cls.signon);
switch (cls.signon) {
case 1:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "prespawn");
break;
case 2:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va ("name \"%s\"\n", cl_name->string));
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message,
va ("color %i %i\n", (cl_color->int_val) >> 4,
(cl_color->int_val) & 15));
MSG_WriteByte (&cls.message, clc_stringcmd);
snprintf (str, sizeof (str), "spawn %s", cls.spawnparms);
MSG_WriteString (&cls.message, str);
break;
case 3:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "begin");
Cache_Report (); // print remaining memory
break;
case 4:
cl.loading = false;
break;
}
}
/*
CL_NextDemo
Called to play the next demo in the demo loop
*/
void
CL_NextDemo (void)
{
if (cls.demonum == -1)
return; // don't play demos
cl.loading = true;
CL_UpdateScreen(cl.time);
if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) {
cls.demonum = 0;
if (!cls.demos[cls.demonum][0]) {
Sys_Printf ("No demos listed with startdemos\n");
cls.demonum = -1;
return;
}
}
Cbuf_InsertText (host_cbuf, va ("playdemo %s\n", cls.demos[cls.demonum]));
cls.demonum++;
}
static void
CL_PrintEntities_f (void)
{
entity_t *ent;
int i;
for (i = 0, ent = cl_entities; i < cl.num_entities; i++, ent++) {
Sys_Printf ("%3i:", i);
if (!ent->model) {
Sys_Printf ("EMPTY\n");
continue;
}
Sys_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n",
ent->model->name, ent->frame, VectorExpand (ent->origin),
VectorExpand (ent->angles));
}
}
/*
CL_ReadFromServer
Read all incoming data from the server
*/
int
CL_ReadFromServer (void)
{
int ret;
cl.oldtime = cl.time;
cl.time += host_frametime;
do {
ret = CL_GetMessage ();
if (ret == -1)
Host_Error ("CL_ReadFromServer: lost server connection");
if (!ret)
break;
cl.last_received_message = realtime;
CL_ParseServerMessage ();
} while (ret && cls.state == ca_connected);
if (cl_shownet->int_val)
Sys_Printf ("\n");
CL_RelinkEntities ();
CL_UpdateTEnts ();
// bring the links up to date
return 0;
}
void
CL_SendCmd (void)
{
usercmd_t cmd;
if (cls.state != ca_connected)
return;
if (cls.signon == SIGNONS) {
CL_BaseMove (&cmd);
// send the unreliable message
CL_SendMove (&cmd);
}
if (cls.demoplayback) {
SZ_Clear (&cls.message);
return;
}
// send the reliable message
if (!cls.message.cursize)
return; // no message at all
if (!NET_CanSendMessage (cls.netcon)) {
Sys_MaskPrintf (SYS_DEV, "CL_WriteToServer: can't send\n");
return;
}
if (NET_SendMessage (cls.netcon, &cls.message) == -1)
Host_Error ("CL_WriteToServer: lost server connection");
SZ_Clear (&cls.message);
}
void
CL_SetState (cactive_t state)
{
cactive_t old_state = cls.state;
cls.state = state;
if (old_state != state) {
if (state == ca_active) {
// entering active state
r_active = true;
Key_SetKeyDest (key_game);
IN_ClearStates ();
VID_SetCaption ("");
} else if (old_state == ca_active) {
// leaving active state
r_active = false;
Key_SetKeyDest (key_console);
VID_SetCaption ("Disconnected");
}
if (state == ca_connected)
S_AmbientOn ();
else
S_AmbientOff ();
}
if (con_module)
con_module->data->console->force_commandline = (state != ca_active);
}
static void
Force_CenterView_f (void)
{
cl.viewangles[PITCH] = 0;
}
void
CL_Init (void)
{
SZ_Alloc (&cls.message, 1024);
CL_Input_Init ();
CL_TEnts_Init ();
CL_ClearState ();
Cmd_AddCommand ("entities", CL_PrintEntities_f, "No Description");
Cmd_AddCommand ("disconnect", CL_Disconnect_f, "No Description");
Cmd_AddCommand ("maplist", Con_Maplist_f, "List available maps");
Cmd_AddCommand ("skyboxlist", Con_Skyboxlist_f, "List skyboxes available");
Cmd_AddCommand ("demolist", Con_Demolist_DEM_f, "List available demos");
Cmd_AddCommand ("force_centerview", Force_CenterView_f, "force the view "
"to be level");
}