]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/csqcmodel/cl_player.qc
Merge branch 'terencehill/slider_drag_fix' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / csqcmodel / cl_player.qc
1 /*
2  * Copyright (c) 2011 Rudolf Polzer
3  * Copyright (c) 2015 Micah Talkiewicz
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 #include "cl_player.qh"
24
25 #include "cl_model.qh"
26 #include "common.qh"
27 #include "interpolate.qh"
28 #include "../../client/defs.qh"
29 #include "../../client/main.qh"
30 #include "../../common/constants.qh"
31 #include "../../common/stats.qh"
32 #include "../../common/triggers/trigger/viewloc.qh"
33 #include "../../common/util.qh"
34 #include "../../common/viewloc.qh"
35
36 float autocvar_cl_movement_errorcompensation = 0;
37 int autocvar_cl_movement = 1;
38
39 // engine stuff
40 float pmove_onground; // weird engine flag we shouldn't really use but have to for now
41
42 vector csqcplayer_origin, csqcplayer_velocity;
43 float csqcplayer_sequence;
44 int player_pmflags;
45 float csqcplayer_moveframe;
46 vector csqcplayer_predictionerroro;
47 vector csqcplayer_predictionerrorv;
48 float csqcplayer_predictionerrortime;
49 float csqcplayer_predictionerrorfactor;
50
51 vector CSQCPlayer_GetPredictionErrorO()
52 {
53         if(time >= csqcplayer_predictionerrortime)
54                 return '0 0 0';
55         return csqcplayer_predictionerroro * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
56 }
57
58 vector CSQCPlayer_GetPredictionErrorV()
59 {
60         if(time >= csqcplayer_predictionerrortime)
61                 return '0 0 0';
62         return csqcplayer_predictionerrorv * (csqcplayer_predictionerrortime - time) * csqcplayer_predictionerrorfactor;
63 }
64
65 void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff)
66 {
67         // error too big to compensate, we LIKELY hit a teleport or a
68         // jumppad, or it's a jump time disagreement that'll get fixed
69         // next frame
70
71         // FIXME we sometimes have disagreement in order of jump velocity. Do not act on them!
72         /*
73         // commented out as this one did not help
74         if(onground_diff)
75         {
76                 printf("ONGROUND MISMATCH: %d x=%v v=%v\n", onground_diff, o, v);
77                 return;
78         }
79         */
80         if(vlen(o) > 32 || vlen(v) > 192)
81         {
82                 //printf("TOO BIG: x=%v v=%v\n", o, v);
83                 return;
84         }
85
86         if(!autocvar_cl_movement_errorcompensation)
87         {
88                 csqcplayer_predictionerrorfactor = 0;
89                 return;
90         }
91
92         csqcplayer_predictionerroro = CSQCPlayer_GetPredictionErrorO() + o;
93         csqcplayer_predictionerrorv = CSQCPlayer_GetPredictionErrorV() + v;
94         csqcplayer_predictionerrorfactor = autocvar_cl_movement_errorcompensation / ticrate;
95         csqcplayer_predictionerrortime = time + 1.0 / csqcplayer_predictionerrorfactor;
96 }
97
98 void CSQCPlayer_Unpredict()
99 {SELFPARAM();
100         if(csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED)
101                 return;
102         if(csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED)
103                 error("Cannot unpredict in current status");
104         self.origin = csqcplayer_origin;
105         self.velocity = csqcplayer_velocity;
106         csqcplayer_moveframe = csqcplayer_sequence+1; //+1 because the recieved frame has the move already done (server side)
107         self.flags = player_pmflags;
108 }
109
110 void CSQCPlayer_SetMinsMaxs()
111 {SELFPARAM();
112         if(self.flags & FL_DUCKED)
113         {
114                 self.mins = PL_CROUCH_MIN;
115                 self.maxs = PL_CROUCH_MAX;
116                 self.view_ofs = PL_CROUCH_VIEW_OFS;
117         }
118         else
119         {
120                 self.mins = PL_MIN;
121                 self.maxs = PL_MAX;
122                 self.view_ofs = PL_VIEW_OFS;
123         }
124 }
125
126 void CSQCPlayer_SavePrediction()
127 {SELFPARAM();
128         player_pmflags = self.flags;
129         csqcplayer_origin = self.origin;
130         csqcplayer_velocity = self.velocity;
131         csqcplayer_sequence = servercommandframe;
132         csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
133 }
134
135 void CSQC_ClientMovement_PlayerMove_Frame();
136
137 void PM_Movement_Move()
138 {SELFPARAM();
139         runstandardplayerphysics(self);
140 #ifdef CSQC
141         self.flags =
142                         ((self.pmove_flags & PMF_DUCKED) ? FL_DUCKED : 0) |
143                         (!(self.pmove_flags & PMF_JUMP_HELD) ? FL_JUMPRELEASED : 0) |
144                         ((self.pmove_flags & PMF_ONGROUND) ? FL_ONGROUND : 0);
145 #endif
146 }
147
148 void CSQCPlayer_Physics(void)
149 {
150         switch(autocvar_cl_movement)
151         {
152                 case 1: CSQC_ClientMovement_PlayerMove_Frame(); break;
153                 case 2: PM_Movement_Move(); break;
154         }
155 }
156
157 void CSQCPlayer_PredictTo(float endframe, float apply_error)
158 {SELFPARAM();
159         CSQCPlayer_Unpredict();
160         if(apply_error)
161         {
162                 self.origin += CSQCPlayer_GetPredictionErrorO();
163                 self.velocity += CSQCPlayer_GetPredictionErrorV();
164         }
165         CSQCPlayer_SetMinsMaxs();
166
167         csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
168
169 #if 0
170         // we don't need this
171         // darkplaces makes servercommandframe == 0 in these cases anyway
172         if (getstatf(STAT_HEALTH) <= 0)
173         {
174                 csqcplayer_moveframe = clientcommandframe;
175                 getinputstate(csqcplayer_moveframe-1);
176                 LOG_INFO("the Weird code path got hit\n");
177                 return;
178         }
179 #endif
180
181         if(csqcplayer_moveframe >= endframe)
182         {
183                 getinputstate(csqcplayer_moveframe - 1);
184         }
185         else
186         {
187                 do
188                 {
189                         if (!getinputstate(csqcplayer_moveframe))
190                                 break;
191                         CSQCPlayer_Physics();
192                         CSQCPlayer_SetMinsMaxs();
193                         csqcplayer_moveframe++;
194                 }
195                 while(csqcplayer_moveframe < endframe);
196         }
197
198         //add in anything that was applied after (for low packet rate protocols)
199         input_angles = view_angles;
200 }
201
202 bool CSQCPlayer_IsLocalPlayer()
203 {SELFPARAM();
204         return (self == csqcplayer);
205 }
206
207 void CSQCPlayer_SetViewLocation()
208 {
209         viewloc_SetViewLocation();
210 }
211
212 void CSQCPlayer_SetCamera()
213 {SELFPARAM();
214         vector v0;
215         v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
216
217         if(csqcplayer)
218         {
219                 setself(csqcplayer);
220
221                 if(servercommandframe == 0 || clientcommandframe == 0)
222                 {
223                         InterpolateOrigin_Do();
224                         self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
225
226                         // get crouch state from the server
227                         if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
228                                 self.flags &= ~FL_DUCKED;
229                         else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
230                                 self.flags |= FL_DUCKED;
231
232                         // get onground state from the server
233                         if(pmove_onground)
234                                 self.flags |= FL_ONGROUND;
235                         else
236                                 self.flags &= ~FL_ONGROUND;
237
238                         CSQCPlayer_SetMinsMaxs();
239
240                         // override it back just in case
241                         self.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
242
243                         // set velocity
244                         self.velocity = v0;
245                 }
246                 else
247                 {
248                         float flg = self.iflags;
249                         self.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES);
250                         InterpolateOrigin_Do();
251                         self.iflags = flg;
252
253                         if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
254                         {
255                                 vector o, v;
256                                 o = self.origin;
257                                 v = v0;
258                                 csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
259                                 CSQCPlayer_PredictTo(servercommandframe + 1, false);
260                                 CSQCPlayer_SetPredictionError(self.origin - o, self.velocity - v, pmove_onground - !!(self.flags & FL_ONGROUND));
261                                 self.origin = o;
262                                 self.velocity = v;
263
264                                 // get crouch state from the server
265                                 if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
266                                         self.flags &= ~FL_DUCKED;
267                                 else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
268                                         self.flags |= FL_DUCKED;
269
270                                 // get onground state from the server
271                                 if(pmove_onground)
272                                         self.flags |= FL_ONGROUND;
273                                 else
274                                         self.flags &= ~FL_ONGROUND;
275
276                                 CSQCPlayer_SavePrediction();
277                         }
278                         CSQCPlayer_PredictTo(clientcommandframe + 1, true);
279
280 #ifdef CSQCMODEL_SERVERSIDE_CROUCH
281                         // get crouch state from the server (LAG)
282                         if(getstati(STAT_VIEWHEIGHT) == PL_VIEW_OFS.z)
283                                 self.flags &= ~FL_DUCKED;
284                         else if(getstati(STAT_VIEWHEIGHT) == PL_CROUCH_VIEW_OFS.z)
285                                 self.flags |= FL_DUCKED;
286 #endif
287
288                         CSQCPlayer_SetMinsMaxs();
289
290                         self.angles_y = input_angles.y;
291                 }
292
293                 // relink
294                 setorigin(self, self.origin);
295
296                 setself(this);
297         }
298
299         entity view = CSQCModel_server2csqc(player_localentnum);
300
301         if(view && view != csqcplayer)
302         {
303                 WITH(entity, self, view, InterpolateOrigin_Do());
304                 view.view_ofs = '0 0 1' * getstati(STAT_VIEWHEIGHT);
305         }
306
307         if(view)
308         {
309                 int refdefflags = 0;
310
311                 if(view.csqcmodel_teleported)
312                         refdefflags |= REFDEFFLAG_TELEPORTED;
313
314                 if(input_buttons & 4)
315                         refdefflags |= REFDEFFLAG_JUMPING;
316
317                 // note: these two only work in WIP2, but are harmless in WIP1
318                 if(getstati(STAT_HEALTH) <= 0)
319                         refdefflags |= REFDEFFLAG_DEAD;
320
321                 if(intermission)
322                         refdefflags |= REFDEFFLAG_INTERMISSION;
323
324                 V_CalcRefdef(view, refdefflags);
325         }
326         else
327         {
328                 // FIXME by CSQC spec we have to do this:
329                 // but it breaks chase cam
330                 /*
331                 setproperty(VF_ORIGIN, pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT));
332                 setproperty(VF_ANGLES, view_angles);
333                 */
334         }
335
336         { CSQCPLAYER_HOOK_POSTCAMERASETUP }
337 }
338
339 void CSQCPlayer_Remove()
340 {
341         csqcplayer = world;
342         cvar_settemp("cl_movement_replay", "1");
343 }
344
345 float CSQCPlayer_PreUpdate()
346 {SELFPARAM();
347         if(self != csqcplayer)
348                 return 0;
349         if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
350                 CSQCPlayer_Unpredict();
351         return 1;
352 }
353
354 float CSQCPlayer_PostUpdate()
355 {SELFPARAM();
356         if(self.entnum != player_localnum + 1)
357                 return 0;
358         csqcplayer = self;
359         csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
360         cvar_settemp("cl_movement_replay", "0");
361         self.entremove = CSQCPlayer_Remove;
362         return 1;
363 }