]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/csqcmodel/cl_player.qc
3ee4afa130be823ed580f5f61293faadd1b143e7
[xonotic/xonotic-data.pk3dir.git] / qcsrc / csqcmodel / 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_predictionerrorcompensation = 0;
24 var float autocvar_chase_active;
25 var float autocvar_chase_back;
26
27 .float pmove_flags;
28
29 entity csqcplayer;
30 vector csqcplayer_origin, csqcplayer_velocity;
31 float csqcplayer_sequence, player_pmflags;
32 float csqcplayer_moveframe;
33 vector csqcplayer_predictionerror;
34 float csqcplayer_predictionerrortime;
35
36 vector CSQCPlayer_GetPredictionError()
37 {
38         if(!autocvar_cl_predictionerrorcompensation)
39                 return '0 0 0';
40         if(time < csqcplayer_predictionerrortime)
41                 return csqcplayer_predictionerror * (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation;
42         return '0 0 0';
43 }
44
45 void CSQCPlayer_SetPredictionError(vector v)
46 {
47         if(!autocvar_cl_predictionerrorcompensation)
48                 return;
49         csqcplayer_predictionerror = (csqcplayer_predictionerrortime - time) * autocvar_cl_predictionerrorcompensation * csqcplayer_predictionerror + v;
50         csqcplayer_predictionerrortime = time + 1.0 / autocvar_cl_predictionerrorcompensation;
51 }
52
53 void CSQCPlayer_Unpredict()
54 {
55         if(csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED)
56                 return;
57         if(csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED)
58                 error("Cannot unpredict in current status");
59         self.origin = csqcplayer_origin;
60         self.velocity = csqcplayer_velocity;
61         csqcplayer_moveframe = csqcplayer_sequence+1; //+1 because the recieved frame has the move already done (server side)
62         self.pmove_flags = player_pmflags;
63 }
64
65 void CSQCPlayer_SavePrediction()
66 {
67         player_pmflags = self.pmove_flags;
68         csqcplayer_origin = self.origin;
69         csqcplayer_velocity = self.velocity;
70         csqcplayer_sequence = servercommandframe;
71         csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
72 }
73
74 void CSQCPlayer_PredictTo(float endframe)
75 {
76         CSQCPlayer_Unpredict();
77
78         csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
79
80         if (getstatf(STAT_HEALTH) <= 0)
81         {
82                 csqcplayer_moveframe = clientcommandframe;
83                 getinputstate(csqcplayer_moveframe-1);
84                 return;
85         }
86
87         while(csqcplayer_moveframe < endframe)
88         {
89                 if (!getinputstate(csqcplayer_moveframe))
90                 {
91                         break;
92                 }
93                 runstandardplayerphysics(self);
94
95                 csqcplayer_moveframe++;
96         }
97
98         //add in anything that was applied after (for low packet rate protocols)
99         input_angles = view_angles;
100 }
101
102 float CSQCPlayer_IsLocalPlayer()
103 {
104         return (self == csqcplayer);
105 }
106
107 void CSQCPlayer_SetCamera()
108 {
109         if(csqcplayer)
110         {
111                 vector org, ang;
112                 entity oldself;
113                 oldself = self;
114                 self = csqcplayer;
115
116                 if(servercommandframe == 0)
117                 {
118                         InterpolateOrigin_Do();
119                 }
120                 else
121                 {
122                         if(csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER)
123                         {
124                                 vector o, v;
125                                 o = self.origin;
126                                 v = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity
127                                 csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED;
128                                 CSQCPlayer_PredictTo(servercommandframe + 1);
129                                 CSQCPlayer_SetPredictionError(o - self.origin);
130                                 self.origin = o;
131                                 self.velocity = v;
132                                 CSQCPlayer_SavePrediction();
133                         }
134                         CSQCPlayer_PredictTo(clientcommandframe);
135                 }
136
137                 self = oldself;
138
139                 org = csqcplayer.origin + '0 0 1' * getstati(STAT_VIEWHEIGHT) + CSQCPlayer_GetPredictionError();
140                 ang = R_SetView3fv(VF_ANGLES);
141
142                 // simulate missing engine features
143                 if(autocvar_chase_active)
144                 {
145                         float dist;
146                         vector chase_dest;
147                         dist = -autocvar_chase_back - 8;
148                         makevectors(ang);
149                         chase_dest = org + v_forward * dist;
150                         traceline(org, chase_dest, MOVE_NOMONSTERS, csqcplayer);
151                         org = trace_endpos + 8 * v_forward + 4 * trace_plane_normal;
152                 }
153
154                 R_SetView3fv(VF_ORIGIN, org);
155                 R_SetView3fv(VF_ANGLES, ang);
156         }
157 }
158
159 void CSQCPlayer_Remove()
160 {
161         if(self.entnum != player_localentnum)
162                 return;
163         csqcplayer = world;
164         cvar_clientsettemp("cl_movement_replay", "1");
165 }
166
167 float CSQCPlayer_PreUpdate()
168 {
169         if(self.entnum != player_localentnum)
170                 return 0;
171         cvar_clientsettemp("cl_movement_replay", "0");
172         if(csqcplayer_status != CSQCPLAYERSTATUS_FROMSERVER)
173                 CSQCPlayer_Unpredict();
174         return 1;
175 }
176
177 float CSQCPlayer_PostUpdate()
178 {
179         if(self.entnum == player_localentnum)
180                 self.renderflags |= RF_EXTERNALMODEL;
181         else
182                 self.renderflags &~= RF_EXTERNALMODEL;
183         if(self.entnum != player_localentnum)
184                 return 0;
185         csqcplayer_status = CSQCPLAYERSTATUS_FROMSERVER;
186         csqcplayer = self;
187         self.entremove = CSQCPlayer_Remove;
188         return 1;
189 }
190
191 entity CSQCPlayer_GetPlayer(float pl)
192 {
193         return findfloat(world, entnum, pl); // FIXME optimize this using an array
194 }