]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hook.qc
Rename defs to qh
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hook.qc
1 #if defined(CSQC)
2         #include "../dpdefs/csprogsdefs.qh"
3         #include "defs.qh"
4         #include "../common/constants.qh"
5         #include "../warpzonelib/common.qh"
6         #include "../common/teams.qh"
7         #include "../common/util.qh"
8         #include "autocvars.qh"
9         #include "../csqcmodellib/interpolate.qh"
10         #include "noise.qh"
11         #include "main.qh"
12         #include "../csqcmodellib/cl_model.qh"
13 #elif defined(MENUQC)
14 #elif defined(SVQC)
15 #endif
16
17
18 .float HookType; // ENT_CLIENT_*
19 .vector origin;
20 .vector velocity;
21 .float HookSilent;
22 .float HookRange;
23
24 void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg)
25 {
26         // I want to draw a quad...
27         // from and to are MIDPOINTS.
28
29         vector axis, thickdir, A, B, C, D;
30         float length_tex;
31
32         axis = normalize(to - from);
33         length_tex = aspect * vlen(to - from) / thickness;
34
35         // direction is perpendicular to the view normal, and perpendicular to the axis
36         thickdir = normalize(cross(axis, vieworg - from));
37
38         A = from - thickdir * (thickness / 2);
39         B = from + thickdir * (thickness / 2);
40         C = to + thickdir * (thickness / 2);
41         D = to - thickdir * (thickness / 2);
42
43         R_BeginPolygon(texture, drawflag);
44         R_PolygonVertex(A, '0 0 0' + shift * '1 0 0', rgb, theAlpha);
45         R_PolygonVertex(B, '0 1 0' + shift * '1 0 0', rgb, theAlpha);
46         R_PolygonVertex(C, '0 1 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha);
47         R_PolygonVertex(D, '0 0 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha);
48         R_EndPolygon();
49 }
50
51 string Draw_GrapplingHook_trace_callback_tex;
52 float Draw_GrapplingHook_trace_callback_rnd;
53 vector Draw_GrapplingHook_trace_callback_rgb;
54 float Draw_GrapplingHook_trace_callback_a;
55 void Draw_GrapplingHook_trace_callback(vector start, vector hit, vector end)
56 {
57         float i;
58         vector vorg;
59         vorg = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
60         for(i = 0; i < Draw_GrapplingHook_trace_callback_a; ++i)
61                 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);
62         Draw_GrapplingHook_trace_callback_rnd += 0.25 * vlen(hit - start) / 8;
63 }
64
65 .float teleport_time;
66 void Draw_GrapplingHook()
67 {
68         vector a, b, atrans;
69         string tex;
70         vector rgb;
71         float t;
72         int s;
73         vector vs;
74         float intensity, offset;
75
76         if(self.teleport_time)
77         if(time > self.teleport_time)
78         {
79                 sound (self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); // safeguard
80                 self.teleport_time = 0;
81         }
82
83         InterpolateOrigin_Do();
84
85         s = autocvar_cl_gunalign;
86         if(s != 1 && s != 2 && s != 4)
87                 s = 3; // default value
88         --s;
89         switch(self.HookType)
90         {
91                 default:
92                 case ENT_CLIENT_HOOK:
93                         vs = hook_shotorigin[s];
94                         break;
95                 case ENT_CLIENT_ARC_BEAM:
96                         vs = lightning_shotorigin[s];
97                         break;
98         }
99
100         if((self.owner.sv_entnum == player_localentnum - 1) && autocvar_chase_active <= 0)
101         {
102                 switch(self.HookType)
103                 {
104                         default:
105                         case ENT_CLIENT_HOOK:
106                                 a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
107                                 b = self.origin;
108                                 break;
109                         case ENT_CLIENT_ARC_BEAM:
110                                 if(self.HookRange)
111                                         b = view_origin + view_forward * self.HookRange;
112                                 else
113                                         b = view_origin + view_forward * vlen(self.velocity - self.origin); // honor original length of beam!
114                                 WarpZone_TraceLine(view_origin, b, MOVE_NORMAL, world);
115                                 b = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
116                                 a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
117                                 break;
118                 }
119         }
120         else
121         {
122                 switch(self.HookType)
123                 {
124                         default:
125                         case ENT_CLIENT_HOOK:
126                                 a = self.velocity;
127                                 b = self.origin;
128                                 break;
129                         case ENT_CLIENT_ARC_BEAM:
130                                 a = self.origin;
131                                 b = self.velocity;
132                                 break;
133                 }
134         }
135
136         t = GetPlayerColorForce(self.owner.sv_entnum);
137
138         switch(self.HookType)
139         {
140                 default:
141                 case ENT_CLIENT_HOOK:
142                         intensity = 1;
143                         offset = 0;
144                         switch(t)
145                         {
146                                 case NUM_TEAM_1: tex = "particles/hook_red"; rgb = '1 0.3 0.3'; break;
147                                 case NUM_TEAM_2: tex = "particles/hook_blue"; rgb = '0.3 0.3 1'; break;
148                                 case NUM_TEAM_3: tex = "particles/hook_yellow"; rgb = '1 1 0.3'; break;
149                                 case NUM_TEAM_4: tex = "particles/hook_pink"; rgb = '1 0.3 1'; break;
150                                 default: tex = "particles/hook_white"; rgb = getcsqcplayercolor(self.sv_entnum); break;
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         int sf = ReadByte();
211
212         self.HookSilent = (sf & 0x80);
213         self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
214
215         InterpolateOrigin_Undo();
216
217         if(sf & 1)
218         {
219                 int myowner = ReadByte();
220                 self.owner = playerslots[myowner - 1];
221                 self.sv_entnum = myowner;
222                 switch(self.HookType)
223                 {
224                         default:
225                         case ENT_CLIENT_HOOK:
226                                 self.HookRange = 0;
227                                 break;
228                         case ENT_CLIENT_ARC_BEAM:
229                                 self.HookRange = ReadCoord();
230                                 break;
231                 }
232         }
233         if(sf & 2)
234         {
235                 self.origin_x = ReadCoord();
236                 self.origin_y = ReadCoord();
237                 self.origin_z = ReadCoord();
238                 setorigin(self, self.origin);
239         }
240         if(sf & 4)
241         {
242                 self.velocity_x = ReadCoord();
243                 self.velocity_y = ReadCoord();
244                 self.velocity_z = ReadCoord();
245         }
246
247         InterpolateOrigin_Note();
248
249         if(bIsNew || !self.teleport_time)
250         {
251                 self.draw = Draw_GrapplingHook;
252                 self.entremove = Remove_GrapplingHook;
253
254                 switch(self.HookType)
255                 {
256                         default:
257                         case ENT_CLIENT_HOOK:
258                                 // for the model
259                                 setmodel(self, "models/hook.md3");
260                                 self.drawmask = MASK_NORMAL;
261                                 break;
262                         case ENT_CLIENT_ARC_BEAM:
263                                 sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
264                                 break;
265                 }
266         }
267
268         self.teleport_time = time + 10;
269 }
270
271 void Hook_Precache()
272 {
273         precache_sound("weapons/lgbeam_fly.wav");
274         precache_model("models/hook.md3");
275 }
276
277 // TODO: hook: temporarily transform self.origin for drawing the model along warpzones!