1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20#include "quakedef.h"
21#include "winquake.h"
22
23cvar_t	cl_nopred = CVAR2("cl_nopred","0");
24cvar_t	cl_pushlatency = CVAR2("pushlatency","-999");
25
26extern	frame_t		*view_frame;
27
28/*
29=================
30CL_NudgePosition
31
32If pmove.origin is in a solid position,
33try nudging slightly on all axis to
34allow for the cut precision of the net coordinates
35=================
36*/
37void CL_NudgePosition (void)
38{
39	vec3_t	base;
40	int		x, y;
41
42	if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY)
43		return;
44
45	VectorCopy (pmove.origin, base);
46	for (x=-1 ; x<=1 ; x++)
47	{
48		for (y=-1 ; y<=1 ; y++)
49		{
50			pmove.origin[0] = base[0] + x * 1.0/8;
51			pmove.origin[1] = base[1] + y * 1.0/8;
52			if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY)
53				return;
54		}
55	}
56	Con_DPrintf ("CL_NudgePosition: stuck\n");
57}
58
59/*
60==============
61CL_PredictUsercmd
62==============
63*/
64void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator)
65{
66	// split up very long moves
67	if (u->msec > 50)
68	{
69		player_state_t	temp;
70		usercmd_t	split;
71
72		split = *u;
73		split.msec /= 2;
74
75		CL_PredictUsercmd (from, &temp, &split, spectator);
76		CL_PredictUsercmd (&temp, to, &split, spectator);
77		return;
78	}
79
80	VectorCopy (from->origin, pmove.origin);
81//	VectorCopy (from->viewangles, pmove.angles);
82	VectorCopy (u->angles, pmove.angles);
83	VectorCopy (from->velocity, pmove.velocity);
84
85	pmove.oldbuttons = from->oldbuttons;
86	pmove.waterjumptime = from->waterjumptime;
87	pmove.dead = cl.stats[STAT_HEALTH] <= 0;
88	pmove.spectator = spectator;
89
90	pmove.cmd = *u;
91
92	PlayerMove ();
93//for (i=0 ; i<3 ; i++)
94//pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125;
95	to->waterjumptime = pmove.waterjumptime;
96	to->oldbuttons = pmove.cmd.buttons;
97	VectorCopy (pmove.origin, to->origin);
98	VectorCopy (pmove.angles, to->viewangles);
99	VectorCopy (pmove.velocity, to->velocity);
100	to->onground = onground;
101
102	to->weaponframe = from->weaponframe;
103}
104
105
106
107/*
108==============
109CL_PredictMove
110==============
111*/
112void CL_PredictMove (void)
113{
114	int			i;
115	float		f;
116	frame_t		*from, *to = NULL;
117	int			oldphysent;
118
119	if (cl_pushlatency.value > 0)
120		Cvar_Set ("pushlatency", "0");
121
122	if (cl.paused)
123		return;
124
125	cl.time = realtime - cls.latency - cl_pushlatency.value*0.001;
126	if (cl.time > realtime)
127		cl.time = realtime;
128
129	if (cl.intermission)
130		return;
131
132	if (!cl.validsequence)
133		return;
134
135	if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1)
136		return;
137
138	VectorCopy (cl.viewangles, cl.simangles);
139
140	// this is the last frame received from the server
141	from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
142
143	// we can now render a frame
144	if (cls.state == ca_onserver)
145	{	// first update is the final signon stage
146		char		text[1024];
147
148		cls.state = ca_active;
149		sprintf (text, "QuakeWorld: %s", cls.servername);
150#ifdef _WIN32
151		SetWindowText (mainwindow, text);
152#endif
153	}
154
155	if (cl_nopred.value)
156	{
157		VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel);
158		VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg);
159		return;
160	}
161
162	// predict forward until cl.time <= to->senttime
163	oldphysent = pmove.numphysent;
164	CL_SetSolidPlayers (cl.playernum);
165
166//	to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
167
168	for (i=1 ; i<UPDATE_BACKUP-1 && cls.netchan.incoming_sequence+i <
169			cls.netchan.outgoing_sequence; i++)
170	{
171		to = &cl.frames[(cls.netchan.incoming_sequence+i) & UPDATE_MASK];
172		CL_PredictUsercmd (&from->playerstate[cl.playernum]
173			, &to->playerstate[cl.playernum], &to->cmd, cl.spectator);
174		if (to->senttime >= cl.time)
175			break;
176		from = to;
177	}
178
179	pmove.numphysent = oldphysent;
180
181	if (i == UPDATE_BACKUP-1 || !to)
182		return;		// net hasn't deliver packets in a long time...
183
184	// now interpolate some fraction of the final frame
185	if (to->senttime == from->senttime)
186		f = 0;
187	else
188	{
189		f = (cl.time - from->senttime) / (to->senttime - from->senttime);
190
191		if (f < 0)
192			f = 0;
193		if (f > 1)
194			f = 1;
195	}
196
197	for (i=0 ; i<3 ; i++)
198		if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128)
199		{	// teleported, so don't lerp
200			VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel);
201			VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg);
202			return;
203		}
204
205	for (i=0 ; i<3 ; i++)
206	{
207		cl.simorg[i] = from->playerstate[cl.playernum].origin[i]
208			+ f*(to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]);
209		cl.simvel[i] = from->playerstate[cl.playernum].velocity[i]
210			+ f*(to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]);
211	}
212}
213
214
215/*
216==============
217CL_InitPrediction
218==============
219*/
220void CL_InitPrediction (void)
221{
222	Cvar_RegisterVariable (&cl_pushlatency);
223	Cvar_RegisterVariable (&cl_nopred);
224}
225
226