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