]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/vehicles/all.qc
e86b0f72b95d0fd96c040d841157178bb8366f73
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / vehicles / all.qc
1 #include "all.qh"
2 #include "../_all.qh"
3
4 #include "../movetypes.qh"
5 #include "../movetypes.qh"
6 #include "../prandom.qh"
7 #include "../scoreboard.qh"
8 #include "../t_items.qh"
9
10 #include "../../common/buffs.qh"
11 #include "../../common/constants.qh"
12 #include "../../common/stats.qh"
13 #include "../../common/util.qh"
14
15 #include "../../csqcmodellib/cl_model.qh"
16
17 .float cnt;
18
19 const string vCROSS_AIM  = "gfx/vehicles/crosshair_aim.tga";
20 const string vCROSS_DROP = "gfx/vehicles/crosshair_drop.tga";
21 const string vCROSS_HEAL = "gfx/vehicles/crosshair_heal.tga";
22 const string vCROSS_HINT = "gfx/vehicles/crosshair_hint.tga";
23 const string vCROSS_LOCK = "gfx/vehicles/crosshair_lock.tga";
24 const string vCROSS_TAG  = "gfx/vehicles/crosshair_tag.tga";
25
26 const int SBRM_FIRST     = 1;
27 const int SBRM_VOLLY     = 1;
28 const int SBRM_GUIDE     = 2;
29 const int SBRM_ARTILLERY = 3;
30 const int SBRM_LAST      = 3;
31
32 const int RSM_FIRST = 1;
33 const int RSM_BOMB  = 1;
34 const int RSM_FLARE = 2;
35 const int RSM_LAST  = 2;
36
37 const int MAX_AXH = 4;
38 entity AuxiliaryXhairs[MAX_AXH];
39 entityclass(AuxiliaryXhair);
40 class(AuxiliaryXhair) .string axh_image;
41 class(AuxiliaryXhair) .float  axh_fadetime;
42 class(AuxiliaryXhair) .float  axh_drawflag;
43
44 entity dropmark;
45
46 float alarm1time;
47 float alarm2time;
48 int weapon2mode;
49
50
51 void AuxiliaryXhair_Draw2D()
52 {
53         if (scoreboard_showscores)
54                 return;
55
56         vector size = draw_getimagesize(self.axh_image) * autocvar_cl_vehicles_crosshair_size;
57         vector pos = project_3d_to_2d(self.move_origin) - 0.5 * size;
58
59         if (!(pos.z < 0 || pos.x < 0 || pos.y < 0 || pos.x > vid_conwidth || pos.y > vid_conheight))
60         {
61                 pos.z = 0;
62                 size.z = 0;
63                 drawpic(pos, self.axh_image, size, self.colormod, autocvar_crosshair_alpha * self.alpha, self.axh_drawflag);
64         }
65
66         if(time - self.cnt > self.axh_fadetime)
67                 self.draw2d = func_null;
68 }
69
70 void Net_AuXair2(bool bIsNew)
71 {
72         int axh_id = bound(0, ReadByte(), MAX_AXH);
73         entity axh = AuxiliaryXhairs[axh_id];
74
75         if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
76         {
77                 axh              = spawn();
78                 axh.draw2d       = func_null;
79                 axh.drawmask     = MASK_NORMAL;
80                 axh.axh_drawflag = DRAWFLAG_ADDITIVE;
81                 axh.axh_fadetime = 0.1;
82                 axh.axh_image    = vCROSS_HINT;
83                 axh.alpha        = 1;
84
85                 AuxiliaryXhairs[axh_id] = axh;
86         }
87
88         axh.move_origin_x = ReadCoord();
89         axh.move_origin_y = ReadCoord();
90         axh.move_origin_z = ReadCoord();
91         axh.colormod_x = ReadByte() / 255;
92         axh.colormod_y = ReadByte() / 255;
93         axh.colormod_z = ReadByte() / 255;
94         axh.cnt    = time;
95         axh.draw2d = AuxiliaryXhair_Draw2D;
96 }
97
98 void Net_VehicleSetup()
99 {
100         int hud_id = ReadByte();
101
102         // Weapon update?
103         if(hud_id > HUD_VEHICLE_LAST)
104         {
105                 weapon2mode = hud_id - HUD_VEHICLE_LAST;
106                 return;
107         }
108
109         // hud_id == 0 means we exited a vehicle, so stop alarm sound/s
110         if(hud_id == 0)
111         {
112                 sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE);
113                 sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE);
114                 return;
115         }
116
117         hud_id  = bound(HUD_VEHICLE_FIRST, hud_id, HUD_VEHICLE_LAST);
118
119         // Init auxiliary crosshairs
120         int i;
121         for(i = 0; i < MAX_AXH; ++i)
122         {
123                 entity axh = AuxiliaryXhairs[i];
124
125                 if(axh != world && !wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
126                         remove(axh);
127
128                 axh              = spawn();
129                 axh.draw2d       = func_null;
130                 axh.drawmask     = MASK_NORMAL;
131                 axh.axh_drawflag = DRAWFLAG_NORMAL;
132                 axh.axh_fadetime = 0.1;
133                 axh.axh_image    = vCROSS_HINT;
134                 axh.alpha        = 1;
135                 AuxiliaryXhairs[i] = axh;
136         }
137
138         switch(hud_id)
139         {
140                 case HUD_SPIDERBOT:
141                         AuxiliaryXhairs[0].axh_image = vCROSS_HINT; // Minigun1
142                         AuxiliaryXhairs[1].axh_image = vCROSS_HINT; // Minigun2
143                         break;
144
145                 case HUD_WAKIZASHI:
146                         AuxiliaryXhairs[0].axh_image = vCROSS_LOCK; // Rocket
147                         break;
148
149                 case HUD_RAPTOR:
150                         AuxiliaryXhairs[1].axh_image = vCROSS_LOCK;
151                         break;
152
153                 case HUD_BUMBLEBEE:
154                         AuxiliaryXhairs[0].axh_image = vCROSS_LOCK; // Raygun-locked
155                         AuxiliaryXhairs[1].axh_image = vCROSS_TAG; // Gunner1
156                         AuxiliaryXhairs[2].axh_image = vCROSS_TAG; // Gunner2
157                         break;
158
159                 case HUD_BUMBLEBEE_GUN:
160                         AuxiliaryXhairs[0].axh_image = vCROSS_TAG; // Plasma cannons
161                         AuxiliaryXhairs[1].axh_image = vCROSS_TAG; // Raygun
162                         break;
163         }
164 }
165
166 void Vehicles_drawHUD(
167         string vehicle,
168         string vehicleWeapon1,
169         string vehicleWeapon2,
170         string iconAmmo1,
171         vector colorAmmo1,
172         string iconAmmo2,
173         vector colorAmmo2,
174         string crosshair)
175 {
176         if(autocvar_r_letterbox)
177                 return;
178
179         if(scoreboard_showscores)
180                 return;
181
182         // Initialize
183         vector hudSize = '0 0 0';
184         vector hudPos  = '0 0 0';
185         vector tmpSize = '0 0 0';
186         vector tmpPos  = '0 0 0';
187
188         float hudAlpha = autocvar_hud_panel_fg_alpha;
189         float barAlpha = autocvar_hud_progressbar_alpha * hudAlpha;
190         float blinkValue = 0.55 + sin(time * 7) * 0.45;
191
192         float health  = getstati(STAT_VEHICLESTAT_HEALTH)  * 0.01;
193         float shield  = getstati(STAT_VEHICLESTAT_SHIELD)  * 0.01;
194         float energy  = getstati(STAT_VEHICLESTAT_ENERGY)  * 0.01;
195         float ammo1   = getstati(STAT_VEHICLESTAT_AMMO1)   * 0.01;
196         float reload1 = getstati(STAT_VEHICLESTAT_RELOAD1) * 0.01;
197         float ammo2   = getstati(STAT_VEHICLESTAT_AMMO2)   * 0.01;
198         float reload2 = getstati(STAT_VEHICLESTAT_RELOAD2) * 0.01;
199
200         // HACK to deal with the inconcistent use of the vehicle stats
201         ammo1 = (ammo1) ? ammo1 : energy;
202
203         // Frame
204         string frame = strcat(hud_skin_path, "/vehicle_frame");
205         if (precache_pic(frame) == "")
206                 frame = "gfx/hud/default/vehicle_frame";
207
208         hudSize  = draw_getimagesize(frame) * autocvar_cl_vehicles_hudscale;
209         hudPos.x = (vid_conwidth - hudSize.x) / 2;
210         hudPos.y = vid_conheight - hudSize.y;
211
212         if(teamplay && autocvar_hud_panel_bg_color_team)
213                 drawpic(hudPos, frame, hudSize, myteamcolors * autocvar_hud_panel_bg_color_team, autocvar_hud_panel_bg_alpha, DRAWFLAG_NORMAL);
214         else
215                 drawpic(hudPos, frame, hudSize, autocvar_hud_panel_bg_color, autocvar_hud_panel_bg_alpha, DRAWFLAG_NORMAL);
216
217         // Model
218         tmpSize.x = hudSize.x / 3;
219         tmpSize.y = hudSize.y;
220         tmpPos.x  = hudPos.x + hudSize.x / 3;
221         tmpPos.y  = hudPos.y;
222
223         if(health < 0.25)
224                 drawpic_skin(tmpPos, vehicle, tmpSize, '1 0 0' + '0 1 1' * blinkValue, hudAlpha, DRAWFLAG_NORMAL);
225         else
226                 drawpic_skin(tmpPos, vehicle, tmpSize, '1 1 1' * health  + '1 0 0' * (1 - health), hudAlpha, DRAWFLAG_NORMAL);
227
228         if(vehicleWeapon1)
229                 drawpic_skin(tmpPos, vehicleWeapon1, tmpSize, '1 1 1' * ammo1 + '1 0 0' * (1 - ammo1), hudAlpha, DRAWFLAG_NORMAL);
230         if(vehicleWeapon2)
231                 drawpic_skin(tmpPos, vehicleWeapon2, tmpSize, '1 1 1' * ammo2 + '1 0 0' * (1 - ammo2), hudAlpha, DRAWFLAG_NORMAL);
232
233         drawpic_skin(tmpPos, "vehicle_shield", tmpSize, '1 1 1' * shield + '1 0 0' * (1 - shield), hudAlpha * shield, DRAWFLAG_NORMAL);
234
235         // Health bar
236         tmpSize.y = hudSize.y / 2;
237         tmpPos.x  = hudPos.x + hudSize.x * (32/768);
238         tmpPos.y  = hudPos.y;
239
240         drawsetcliparea(tmpPos.x + (tmpSize.x * (1 - health)), tmpPos.y, tmpSize.x, tmpSize.y);
241         drawpic_skin(tmpPos, "vehicle_bar_northwest", tmpSize, autocvar_hud_progressbar_health_color, barAlpha, DRAWFLAG_NORMAL);
242
243         // Shield bar
244         tmpPos.y = hudPos.y + hudSize.y / 2;
245
246         drawsetcliparea(tmpPos.x + (tmpSize.x * (1 - shield)), tmpPos.y, tmpSize.x, tmpSize.y);
247         drawpic_skin(tmpPos, "vehicle_bar_southwest", tmpSize, autocvar_hud_progressbar_armor_color, barAlpha, DRAWFLAG_NORMAL);
248
249         // Ammo1 bar
250         tmpPos.x = hudPos.x + hudSize.x * (480/768);
251         tmpPos.y = hudPos.y;
252
253         if(ammo1)
254                 drawsetcliparea(tmpPos.x, tmpPos.y, tmpSize.x * ammo1, tmpSize.y);
255         else
256                 drawsetcliparea(tmpPos.x, tmpPos.y, tmpSize.x * reload1, tmpSize.y);
257
258         drawpic_skin(tmpPos, "vehicle_bar_northeast", tmpSize, colorAmmo1, barAlpha, DRAWFLAG_NORMAL);
259
260         // Ammo2 bar
261         tmpPos.y = hudPos.y + hudSize.y / 2;
262
263         if(ammo2)
264                 drawsetcliparea(tmpPos.x, tmpPos.y, tmpSize.x * ammo2, tmpSize.y);
265         else
266                 drawsetcliparea(tmpPos.x, tmpPos.y, tmpSize.x * reload2, tmpSize.y);
267
268         drawpic_skin(tmpPos, "vehicle_bar_southeast", tmpSize, colorAmmo2, barAlpha, DRAWFLAG_NORMAL);
269         drawresetcliparea();
270
271         // Health icon
272         tmpSize.x = hudSize.x * (80/768);
273         tmpSize.y = hudSize.y * (80/256);
274         tmpPos.x  = hudPos.x + hudSize.x * (64/768);
275         tmpPos.y  = hudPos.y + hudSize.y * (48/256);
276
277         if(health < 0.25)
278         {
279                 if(alarm1time < time)
280                 {
281                         alarm1time = time + 2;
282                         sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTEN_NONE);
283                 }
284                 drawpic_skin(tmpPos, "vehicle_icon_health", tmpSize, '1 1 1', hudAlpha * blinkValue, DRAWFLAG_NORMAL);
285         }
286         else
287         {
288                 if(alarm1time)
289                 {
290                         sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE);
291                         alarm1time = 0;
292                 }
293                 drawpic_skin(tmpPos, "vehicle_icon_health", tmpSize, '1 1 1', hudAlpha, DRAWFLAG_NORMAL);
294         }
295
296         // Shield icon
297         tmpPos.y = hudPos.y + hudSize.y / 2;
298
299         if(shield < 0.25)
300         {
301                 if(alarm2time < time)
302                 {
303                         alarm2time = time + 1;
304                         sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTEN_NONE);
305                 }
306                 drawpic_skin(tmpPos, "vehicle_icon_shield", tmpSize, '1 1 1', hudAlpha * blinkValue, DRAWFLAG_NORMAL);
307         }
308         else
309         {
310                 if(alarm2time)
311                 {
312                         sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTEN_NONE);
313                         alarm2time = 0;
314                 }
315                 drawpic_skin(tmpPos, "vehicle_icon_shield", tmpSize, '1 1 1', hudAlpha, DRAWFLAG_NORMAL);
316         }
317
318         // Ammo1 icon
319         tmpPos.x = hudPos.x + hudSize.x * (624/768);
320         tmpPos.y = hudPos.y + hudSize.y * (48/256);
321
322         if(iconAmmo1)
323         {
324                 if(ammo1)
325                         drawpic_skin(tmpPos, iconAmmo1, tmpSize, '1 1 1', hudAlpha, DRAWFLAG_NORMAL);
326                 else
327                         drawpic_skin(tmpPos, iconAmmo1, tmpSize, '1 1 1', hudAlpha * 0.2, DRAWFLAG_NORMAL);
328         }
329
330         // Ammo2 icon
331         tmpPos.y = hudPos.y + hudSize.y / 2;
332
333         if(iconAmmo2)
334         {
335                 if(ammo2)
336                         drawpic_skin(tmpPos, iconAmmo2, tmpSize, '1 1 1', hudAlpha, DRAWFLAG_NORMAL);
337                 else
338                         drawpic_skin(tmpPos, iconAmmo2, tmpSize, '1 1 1', hudAlpha * 0.2, DRAWFLAG_NORMAL);
339         }
340
341         // Bumblebee gunner crosshairs
342         if(hud == HUD_BUMBLEBEE)
343         {
344                 tmpSize = '1 1 1' * hud_fontsize;
345                 tmpPos.x = hudPos.x + hudSize.x * (520/768);
346
347                 if(!AuxiliaryXhairs[1].draw2d)
348                 {
349                         tmpPos.y = hudPos.y + hudSize.y * (96/256) - tmpSize.y;
350                         drawstring(tmpPos, _("No right gunner!"), tmpSize, '1 1 1', hudAlpha * blinkValue, DRAWFLAG_NORMAL);
351                 }
352
353                 if(!AuxiliaryXhairs[2].draw2d)
354                 {
355                         tmpPos.y = hudPos.y + hudSize.y * (160/256);
356                         drawstring(tmpPos, _("No left gunner!"), tmpSize, '1 1 1', hudAlpha * blinkValue, DRAWFLAG_NORMAL);
357                 }
358         }
359
360         // Raptor bomb crosshair
361         if(hud == HUD_RAPTOR && weapon2mode != RSM_FLARE)
362         {
363                 vector where;
364
365                 if(!dropmark)
366                 {
367                         dropmark = spawn();
368                         dropmark.owner = self;
369                         dropmark.gravity = 1;
370                 }
371
372                 if(reload2 == 1)
373                 {
374                         setorigin(dropmark, pmove_org);
375                         dropmark.velocity = pmove_vel;
376                         tracetoss(dropmark, self);
377
378                         where = project_3d_to_2d(trace_endpos);
379
380                         setorigin(dropmark, trace_endpos);
381                         tmpSize = draw_getimagesize(vCROSS_DROP) * autocvar_cl_vehicles_crosshair_size;
382
383                         if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight))
384                         {
385                                 where.x -= tmpSize.x * 0.5;
386                                 where.y -= tmpSize.y * 0.5;
387                                 where.z = 0;
388                                 drawpic(where, vCROSS_DROP, tmpSize, '0 1 0', autocvar_crosshair_alpha * 0.9, DRAWFLAG_ADDITIVE);
389                                 drawpic(where, vCROSS_DROP, tmpSize, '0 1 0', autocvar_crosshair_alpha * 0.6, DRAWFLAG_NORMAL); // Ensure visibility against bright bg
390                         }
391                         dropmark.cnt = time + 5;
392                 }
393                 else
394                 {
395                         if(dropmark.cnt > time)
396                         {
397                                 where = project_3d_to_2d(dropmark.origin);
398                                 tmpSize = draw_getimagesize(vCROSS_DROP) * autocvar_cl_vehicles_crosshair_size * 1.25;
399
400                                 if (!(where.z < 0 || where.x < 0 || where.y < 0 || where.x > vid_conwidth || where.y > vid_conheight))
401                                 {
402                                         where.x -= tmpSize.x * 0.5;
403                                         where.y -= tmpSize.y * 0.5;
404                                         where.z = 0;
405                                         drawpic(where, vCROSS_DROP, tmpSize, '1 0 0', autocvar_crosshair_alpha * 0.9, DRAWFLAG_ADDITIVE);
406                                         drawpic(where, vCROSS_DROP, tmpSize, '1 0 0', autocvar_crosshair_alpha * 0.6, DRAWFLAG_NORMAL); // Ensure visibility against bright bg
407                                 }
408                         }
409                 }
410         }
411
412         // Crosshair
413         if(crosshair)
414         {
415                 tmpSize  = draw_getimagesize(crosshair) * autocvar_cl_vehicles_crosshair_size;
416                 tmpPos.x = (vid_conwidth - tmpSize.x) / 2;
417                 tmpPos.y = (vid_conheight - tmpSize.y) / 2;
418
419                 drawpic(tmpPos, crosshair, tmpSize, '1 1 1', autocvar_crosshair_alpha, DRAWFLAG_NORMAL);
420         }
421 }
422
423 void CSQC_BUMBLE_HUD()
424 {
425         Vehicles_drawHUD("vehicle_bumble", "vehicle_bumble_weapon1", "vehicle_bumble_weapon2",
426                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
427                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
428                 vCROSS_HEAL);
429 }
430
431 void CSQC_BUMBLE_GUN_HUD()
432 {
433         Vehicles_drawHUD("vehicle_gunner", "vehicle_gunner_weapon1", string_null,
434                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
435                 string_null, '0 0 0',
436                 string_null);
437 }
438
439 void CSQC_SPIDER_HUD()
440 {
441         string crosshair;
442
443         switch(weapon2mode)
444         {
445                 case SBRM_VOLLY: crosshair = vCROSS_LOCK; break;
446                 case SBRM_GUIDE: crosshair = vCROSS_AIM;  break;
447                 default:         crosshair = vCROSS_TAG;
448         }
449
450         Vehicles_drawHUD("vehicle_spider", "vehicle_spider_weapon1", "vehicle_spider_weapon2",
451                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
452                 "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
453                 crosshair);
454 }
455
456 void CSQC_RAPTOR_HUD()
457 {
458         string crosshair;
459
460         switch(weapon2mode)
461         {
462                 case RSM_FLARE: crosshair = vCROSS_LOCK; break;
463                 default:        crosshair = vCROSS_AIM;
464         }
465
466         Vehicles_drawHUD("vehicle_raptor", "vehicle_raptor_weapon1", "vehicle_raptor_weapon2",
467                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
468                 "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
469                 crosshair);
470 }
471
472 void CSQC_WAKIZASHI_HUD()
473 {
474         Vehicles_drawHUD("vehicle_racer", "vehicle_racer_weapon1", "vehicle_racer_weapon2",
475                 "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
476                 "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
477                 vCROSS_AIM);
478 }
479
480 void Vehicles_Precache()
481 {
482         precache_model("models/vehicles/bomblet.md3");
483         precache_model("models/vehicles/clusterbomb.md3");
484         precache_model("models/vehicles/clusterbomb_fragment.md3");
485         precache_model("models/vehicles/rocket01.md3");
486         precache_model("models/vehicles/rocket02.md3");
487
488         precache_sound("vehicles/alarm.wav");
489         precache_sound("vehicles/alarm_shield.wav");
490 }
491
492 void RaptorCBShellfragDraw()
493 {
494         if(wasfreed(self))
495                 return;
496
497         Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
498         self.move_avelocity += randomvec() * 15;
499         self.renderflags = 0;
500
501         if(self.cnt < time)
502                 self.alpha = bound(0, self.nextthink - time, 1);
503
504         if(self.alpha < ALPHA_MIN_VISIBLE)
505                 remove(self);
506 }
507
508 void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
509 {
510         entity sfrag;
511
512         sfrag = spawn();
513         setmodel(sfrag, "models/vehicles/clusterbomb_fragment.md3");
514         setorigin(sfrag, _org);
515
516         sfrag.move_movetype = MOVETYPE_BOUNCE;
517         sfrag.gravity = 0.15;
518         sfrag.solid = SOLID_CORPSE;
519
520         sfrag.draw = RaptorCBShellfragDraw;
521
522         sfrag.move_origin = sfrag.origin = _org;
523         sfrag.move_velocity = _vel;
524         sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity);
525         sfrag.angles = self.move_angles = _ang;
526
527         sfrag.move_time = time;
528         sfrag.damageforcescale = 4;
529
530         sfrag.nextthink = time + 3;
531         sfrag.cnt = time + 2;
532         sfrag.alpha = 1;
533         sfrag.drawmask = MASK_NORMAL;
534 }