Merge remote branch 'refs/remotes/origin/terencehill/quick_cvar_revert'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hook.qc
1 .float HookType; // ENT_CLIENT_*
2 .vector origin;
3 .vector velocity;
4 .float HookSound;
5 .float HookSilent;
6 .float HookRange;
7
8 void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float alpha, float drawflag)
9 {
10         // I want to draw a quad...
11         // from and to are MIDPOINTS.
12         
13         vector axis, thickdir, A, B, C, D;
14         float length_tex;
15
16         axis = normalize(to - from);
17         length_tex = aspect * vlen(to - from) / thickness;
18
19         // direction is perpendicular to the view normal, and perpendicular to the axis
20         thickdir = normalize(cross(axis, view_origin - from));
21
22 /*
23         print("from ", vtos(from), "\n");
24         print("to ", vtos(to), "\n");
25         print("org ", vtos(view_origin), "\n");
26         print("dir ", vtos(thickdir), "\n");
27 */
28
29         A = from - thickdir * (thickness / 2);
30         B = from + thickdir * (thickness / 2);
31         C = to + thickdir * (thickness / 2);
32         D = to - thickdir * (thickness / 2);
33
34         R_BeginPolygon(texture, drawflag);
35         R_PolygonVertex(A, '0 0 0' + shift * '1 0 0', rgb, alpha);
36         R_PolygonVertex(B, '0 1 0' + shift * '1 0 0', rgb, alpha);
37         R_PolygonVertex(C, '0 1 0' + (shift + length_tex) * '1 0 0', rgb, alpha);
38         R_PolygonVertex(D, '0 0 0' + (shift + length_tex) * '1 0 0', rgb, alpha);
39         R_EndPolygon();
40 }
41
42 string Draw_GrapplingHook_trace_callback_tex;
43 float Draw_GrapplingHook_trace_callback_rnd;
44 vector Draw_GrapplingHook_trace_callback_rgb;
45 float Draw_GrapplingHook_trace_callback_a;
46 void Draw_GrapplingHook_trace_callback(vector start, vector hit, vector end)
47 {
48         float i;
49         for(i = 0; i < Draw_GrapplingHook_trace_callback_a; ++i)
50                 Draw_CylindricLine(hit, start, 8, Draw_GrapplingHook_trace_callback_tex, 0.25, Draw_GrapplingHook_trace_callback_rnd, Draw_GrapplingHook_trace_callback_rgb, min(1, Draw_GrapplingHook_trace_callback_a - i), DRAWFLAG_NORMAL);
51         Draw_GrapplingHook_trace_callback_rnd += 0.25 * vlen(hit - start) / 8;
52 }
53
54 void Draw_GrapplingHook()
55 {
56         vector a, b, atrans;
57         string tex, snd;
58         vector rgb;
59         float t;
60         float s;
61         vector vs;
62         float intensity, offset;
63
64         InterpolateOrigin_Do();
65
66         s = cvar("cl_gunalign");
67         if(s != 1 && s != 2 && s != 4)
68                 s = 3; // default value
69         --s;
70         switch(self.HookType)
71         {
72                 default:
73                 case ENT_CLIENT_HOOK:
74                         vs = hook_shotorigin[s];
75                         break;
76                 case ENT_CLIENT_LGBEAM:
77                         vs = electro_shotorigin[s];
78                         break;
79                 case ENT_CLIENT_GAUNTLET:
80                         vs = gauntlet_shotorigin[s];
81                         break;
82         }
83
84         if((self.owner.sv_entnum == player_localentnum - 1))
85         {
86                 switch(self.HookType)
87                 {
88                         default:
89                         case ENT_CLIENT_HOOK:
90                                 a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
91                                 b = self.origin;
92                                 break;
93                         case ENT_CLIENT_LGBEAM:
94                         case ENT_CLIENT_GAUNTLET:
95                                 if(self.HookRange)
96                                         b = view_origin + view_forward * self.HookRange;
97                                 else
98                                         b = view_origin + view_forward * vlen(self.velocity - self.origin); // honor original length of beam!
99                                 WarpZone_TraceLine(view_origin, b, MOVE_NORMAL, world);
100                                 b = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
101                                 a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
102                                 break;
103                 }
104         }
105         else
106         {
107                 switch(self.HookType)
108                 {
109                         default:
110                         case ENT_CLIENT_HOOK:
111                                 a = self.velocity;
112                                 b = self.origin;
113                                 break;
114                         case ENT_CLIENT_LGBEAM:
115                         case ENT_CLIENT_GAUNTLET:
116                                 a = self.origin;
117                                 b = self.velocity;
118                                 break;
119                 }
120         }
121
122         t = GetPlayerColorForce(self.owner.sv_entnum);
123
124         switch(self.HookType)
125         {
126                 default:
127                 case ENT_CLIENT_HOOK:
128                         intensity = 1;
129                         offset = 0;
130                         if(t == COLOR_TEAM1)
131                         {
132                                 tex = "particles/hook_red";
133                                 rgb = '1 .3 .3';
134                         }
135                         else if(t == COLOR_TEAM2)
136                         {
137                                 tex = "particles/hook_blue";
138                                 rgb = '.3 .3 1';
139                         }
140                         else if(t == COLOR_TEAM3)
141                         {
142                                 tex = "particles/hook_yellow";
143                                 rgb = '1 1 .3';
144                         }
145                         else if(t == COLOR_TEAM4)
146                         {
147                                 tex = "particles/hook_pink";
148                                 rgb = '1 .3 1';
149                         }
150                         else
151                         {
152                                 tex = "particles/hook_green";
153                                 rgb = '.3 1 .3';
154                         }
155                         break;
156                 case ENT_CLIENT_LGBEAM:
157                         intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2);
158                         offset = Noise_Brown(self, frametime) * 10;
159                         tex = "particles/lgbeam";
160                         rgb = '1 1 1';
161                         break;
162                 case ENT_CLIENT_GAUNTLET:
163                         intensity = 1;
164                         offset = Noise_White(self, frametime);
165                         tex = "particles/gauntletbeam";
166                         rgb = '1 1 1';
167                         break;
168         }
169
170         Draw_GrapplingHook_trace_callback_tex = tex;
171         Draw_GrapplingHook_trace_callback_rnd = offset;
172         Draw_GrapplingHook_trace_callback_rgb = rgb;
173         Draw_GrapplingHook_trace_callback_a = intensity;
174         WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((self.HookType == ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback);
175         Draw_GrapplingHook_trace_callback_tex = string_null;
176
177         atrans = WarpZone_TransformOrigin(WarpZone_trace_transform, a);
178
179         switch(self.HookType)
180         {
181                 default:
182                 case ENT_CLIENT_HOOK:
183                         setorigin(self, trace_endpos); // hook endpoint!
184                         self.angles = vectoangles(trace_endpos - atrans);
185                         break;
186                 case ENT_CLIENT_LGBEAM:
187                 case ENT_CLIENT_GAUNTLET:
188                         setorigin(self, a); // beam origin!
189                         break;
190         }
191
192         switch(self.HookType)
193         {
194                 default:
195                 case ENT_CLIENT_HOOK:
196                         break;
197                 case ENT_CLIENT_LGBEAM:
198                         pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity);
199                         break;
200                 case ENT_CLIENT_GAUNTLET:
201                         pointparticles(particleeffectnum("gauntlet_lightning"), b, normalize(a - b), frametime * intensity);
202                         break;
203         }
204 }
205
206 void Remove_GrapplingHook()
207 {
208         sound (self, CHAN_PROJECTILE, "misc/null.wav", VOL_BASE, ATTN_NORM);
209 }
210
211 void Ent_ReadHook(float bIsNew, float type)
212 {
213         self.HookType = type;
214
215         float sf;
216         sf = ReadByte();
217
218         self.HookSilent = (sf & 0x80);
219         self.iflags = IFLAG_VELOCITY;
220
221         InterpolateOrigin_Undo();
222
223         if(sf & 1)
224         {
225                 self.owner = playerslots[ReadByte() - 1];
226                 switch(self.HookType)
227                 {
228                         default:
229                         case ENT_CLIENT_HOOK:
230                         case ENT_CLIENT_GAUNTLET:
231                                 self.HookRange = 0;
232                                 break;
233                         case ENT_CLIENT_LGBEAM:
234                                 self.HookRange = ReadCoord();
235                                 break;
236                 }
237         }
238         if(sf & 2)
239         {
240                 self.origin_x = ReadCoord();
241                 self.origin_y = ReadCoord();
242                 self.origin_z = ReadCoord();
243                 setorigin(self, self.origin);
244         }
245         if(sf & 4)
246         {
247                 self.velocity_x = ReadCoord();
248                 self.velocity_y = ReadCoord();
249                 self.velocity_z = ReadCoord();
250         }
251
252         InterpolateOrigin_Note();
253
254         if(bIsNew)
255         {
256                 self.draw = Draw_GrapplingHook;
257                 self.entremove = Remove_GrapplingHook;
258
259                 switch(self.HookType)
260                 {
261                         default:
262                         case ENT_CLIENT_HOOK:
263                                 // for the model
264                                 setmodel(self, "models/hook.md3");
265                                 self.drawmask = MASK_NORMAL;
266                                 break;
267                         case ENT_CLIENT_LGBEAM:
268                                 sound (self, CHAN_PROJECTILE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTN_NORM);
269                                 break;
270                         case ENT_CLIENT_GAUNTLET:
271                                 sound (self, CHAN_PROJECTILE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTN_NORM);
272                                 break;
273                 }
274         }
275 }
276
277 void Hook_Precache()
278 {
279         precache_sound("weapons/lgbeam_fly.wav");
280         precache_sound("weapons/gauntletbeam_fly.wav");
281         precache_model("models/hook.md3");
282 }
283
284 // TODO: hook: temporarily transform self.origin for drawing the model along warpzones!