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