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