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