]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/vehicles/racer.qc
save state before removing stale code
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / vehicles / racer.qc
1 #define RACER_MIN '-120 -120 -40'
2 #define RACER_MAX '120 120 40'
3 #define RACER_TICRATE 0.05
4
5 void racer_exit(float eject);
6 void racer_enter();
7
8 // Auto cvars
9 float autocvar_g_vehicle_racer_speed_afterburn;
10 float autocvar_g_vehicle_racer_afterburn_cost;
11
12 float autocvar_g_vehicle_racer_anglestabilizer;
13 float autocvar_g_vehicle_racer_downforce;
14
15 float autocvar_g_vehicle_racer_speed_forward;
16 float autocvar_g_vehicle_racer_speed_strafe;
17 float autocvar_g_vehicle_racer_springlength;
18 float autocvar_g_vehicle_racer_upforcedamper;
19
20 float autocvar_g_vehicle_racer_hovertype;
21 float autocvar_g_vehicle_racer_hoverpower;
22
23 float autocvar_g_vehicle_racer_turnroll;
24 float autocvar_g_vehicle_racer_turnspeed;
25 float autocvar_g_vehicle_racer_pitchspeed;
26
27 float autocvar_g_vehicle_racer_energy;
28 float autocvar_g_vehicle_racer_energy_regen;
29 float autocvar_g_vehicle_racer_energy_regen_pause;
30
31 float autocvar_g_vehicle_racer_health;
32 float autocvar_g_vehicle_racer_health_regen;
33 float autocvar_g_vehicle_racer_health_regen_pause;
34
35 float autocvar_g_vehicle_racer_shield;
36 float autocvar_g_vehicle_racer_shield_regen;
37 float autocvar_g_vehicle_racer_shield_regen_pause;
38
39 float autocvar_g_vehicle_racer_laser_cost;
40 float autocvar_g_vehicle_racer_laser_damage;
41 float autocvar_g_vehicle_racer_laser_radius;
42 float autocvar_g_vehicle_racer_laser_refire;
43 float autocvar_g_vehicle_racer_laser_speed;
44
45 float autocvar_g_vehicle_racer_rocket_accel;
46 float autocvar_g_vehicle_racer_rocket_damage;
47 float autocvar_g_vehicle_racer_rocket_radius;
48 float autocvar_g_vehicle_racer_rocket_refire;
49 float autocvar_g_vehicle_racer_rocket_speed;
50 float autocvar_g_vehicle_racer_rocket_turnrate;
51 float autocvar_g_vehicle_racer_rocket_locktime;
52
53 float autocvar_g_vehicle_racer_respawntime;
54 float autocvar_g_vehicle_racer_collision_multiplier;
55
56 var vector racer_force_from_tag(string tag_name, float spring_length, float max_power);
57
58 void racer_align4point()
59 {
60     vector push_vector, v_add;
61     float fl_push, fr_push, bl_push, br_push;
62
63     push_vector  = racer_force_from_tag("tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
64     fr_push      = force_fromtag_normpower;
65     vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier);
66
67     push_vector += racer_force_from_tag("tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
68     fl_push      = force_fromtag_normpower;
69     vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier);
70
71     push_vector += racer_force_from_tag("tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
72     br_push      = force_fromtag_normpower;
73     vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier);
74
75     push_vector += racer_force_from_tag("tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
76     bl_push      = force_fromtag_normpower;
77     vehicles_sweap_collision(force_fromtag_origin, self.velocity, frametime, v_add, autocvar_g_vehicle_racer_collision_multiplier);
78
79     self.velocity += (push_vector * frametime);
80
81     // Anti ocilation
82     if(self.velocity_z  > 0)
83         self.velocity_z  *= autocvar_g_vehicle_racer_upforcedamper;
84
85     self.velocity += v_add;
86     self.velocity_z -= autocvar_sv_gravity * frametime;
87
88     push_vector_x =  (fl_push - bl_push);
89     push_vector_x += (fr_push - br_push);
90     push_vector_x *= 360;
91
92     push_vector_z = (fr_push - fl_push);
93     push_vector_z += (br_push - bl_push);
94     push_vector_z *= 360;
95
96     //if (push_vector_z != 0)
97         if(self.angles_z > 0)
98             self.angles_z = max(0, self.angles_z - (autocvar_g_vehicle_racer_anglestabilizer * frametime));
99         else
100             self.angles_z = min(0, self.angles_z + (autocvar_g_vehicle_racer_anglestabilizer * frametime));
101     //else
102         self.angles_z += push_vector_z * frametime;
103
104     //if (push_vector_x != 0)
105         if(self.angles_x > 0)
106             self.angles_x = max(0, self.angles_x - (autocvar_g_vehicle_racer_anglestabilizer * frametime));
107         else
108             self.angles_x = min(0, self.angles_x + (autocvar_g_vehicle_racer_anglestabilizer * frametime));
109     //else
110         self.angles_x += push_vector_x * frametime;
111 }
112
113 void racer_rocket_explode()
114 {
115         PROJECTILE_TOUCH;
116     if(other.owner == self.owner)
117         return;
118
119         self.event_damage = SUB_Null;
120
121     sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
122     pointparticles(particleeffectnum("rocket_explode"), findbetterlocation (self.origin, 16), '0 0 0', 1);
123     RadiusDamage (self, self.realowner ,autocvar_g_vehicle_racer_rocket_damage, 0, autocvar_g_vehicle_racer_rocket_radius, world, 150, DEATH_WAKIROCKET, world);
124
125     remove (self);
126 }
127
128 void racer_bolt_explode()
129 {
130         PROJECTILE_TOUCH;
131     pointparticles(particleeffectnum("wakizashi_gun_impact"), findbetterlocation (self.origin, 8), trace_plane_normal * 1000, 1);
132     RadiusDamage (self, self.realowner, autocvar_g_vehicle_racer_laser_damage, 0, autocvar_g_vehicle_racer_laser_radius, world, 150, DEATH_WAKIGUN, world);
133     sound (self, CHAN_PROJECTILE, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
134     remove (self);
135 }
136
137 void racer_fire_cannon(string tagname)
138 {
139     entity bolt;
140     vector v;
141
142     bolt = spawn();
143     sound (self, CHAN_WEAPON, "weapons/lasergun_fire.wav", VOL_BASE, ATTN_NORM);
144
145     setorigin(bolt, gettaginfo(self, gettagindex(self, tagname)));
146     PROJECTILE_MAKETRIGGER(bolt);
147
148     bolt.movetype        = MOVETYPE_FLYMISSILE;
149     bolt.flags           = FL_PROJECTILE | FL_NOTARGET;
150     bolt.owner           = self;
151     bolt.realowner       = self.owner;
152     bolt.touch           = racer_bolt_explode;
153     bolt.think           = racer_bolt_explode;
154     bolt.nextthink       = time + 9;
155
156     bolt.bot_dodge       = TRUE;
157     bolt.bot_dodgerating = autocvar_g_vehicle_racer_laser_damage;
158
159         // Fix z-aim (for chase mode)
160     v = normalize(trace_endpos - bolt.origin);
161     v_forward_z = v_z * 0.5;
162
163     bolt.velocity = v_forward * autocvar_g_vehicle_racer_laser_speed;
164
165     pointparticles(particleeffectnum("wakizashi_gun_muzzleflash"), bolt.origin, bolt.velocity, 1);
166
167     CSQCProjectile(bolt, TRUE, PROJECTILE_LASER, TRUE);
168 }
169
170 void racer_rocket_groundhugger()
171 {
172     vector olddir, newdir;
173     float oldvel, newvel;
174
175     self.nextthink  = time;
176
177     if ((self.owner.deadflag != DEAD_NO) || (self.cnt < time))
178     {
179         racer_rocket_explode();
180         return;
181     }
182
183     if not (self.realowner.vehicle)
184     {
185         UpdateCSQCProjectile(self);
186         return;
187     }
188
189     olddir = normalize(self.velocity);
190     oldvel = vlen(self.velocity);
191     newvel = oldvel + self.lip;
192
193     tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self);
194     if(trace_fraction <= 0.5)
195     {
196         // Hitting somethign soon, just speed ahead
197         self.velocity = olddir * newvel;
198         UpdateCSQCProjectile(self);
199         return;
200     }
201
202     if(self.enemy != world)
203     {
204         newdir = normalize(self.enemy.origin - self.origin);
205         //self.realowner.vehicle.delay = time + 0.1;
206     }
207
208
209     traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self);
210     if(trace_fraction != 1.0)
211     {
212         newdir += normalize(trace_endpos + '0 0 64' - self.origin);
213         self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
214     }
215     else
216     {
217         self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
218         self.velocity_z -= 800 * sys_frametime;
219     }
220
221
222     UpdateCSQCProjectile(self);
223     return;
224
225 }
226
227 void racer_fire_rocket(string tagname, entity trg)
228 {
229     entity rocket;
230
231     sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", VOL_BASE, ATTN_NORM);
232
233     rocket = spawn();
234     setsize (rocket, '-1 -1 -1', '1 1 1');
235     setorigin(rocket, gettaginfo(self, gettagindex(self, tagname)));
236
237     rocket.lip              = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
238     rocket.wait             = autocvar_g_vehicle_racer_rocket_turnrate;
239     rocket.solid            = SOLID_BBOX;
240     rocket.movetype         = MOVETYPE_FLYMISSILE;
241     rocket.flags            = FL_PROJECTILE;
242     rocket.owner            = self;
243     rocket.realowner        = self.owner;
244     rocket.touch            = racer_rocket_explode;
245     rocket.bot_dodge        = TRUE;
246     rocket.bot_dodgerating  = autocvar_g_vehicle_racer_rocket_damage;
247     rocket.cnt              = time + 9;
248     rocket.velocity         = v_forward * autocvar_g_vehicle_racer_rocket_speed;
249     rocket.think            = racer_rocket_groundhugger;
250     rocket.nextthink        = time + 0.5;
251     rocket.enemy            = trg;
252
253     CSQCProjectile(rocket, TRUE, PROJECTILE_ROCKET, TRUE);
254 }
255
256 .float fire1_waspressed;
257 .float fire2_waspressed;
258 float racer_frame()
259 {
260     entity player, racer;
261     float ftmp, ftmp2;
262     vector df;
263
264     player          = self;
265     racer           = self.vehicle;
266
267     player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0;
268
269     self = racer;
270
271     if(player.BUTTON_USE)
272     {
273         vehicles_exit(VHEF_NORMAL);
274         self = player;
275         return 0;
276     }
277
278     if(racer.deadflag != DEAD_NO)
279     {
280         self = player;
281         player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
282         return 1;
283     }
284
285     racer_align4point();
286
287     racer.angles_x *= -1;
288     makevectors(racer.angles);
289
290     // Yaw
291     ftmp = autocvar_g_vehicle_racer_turnspeed * sys_frametime;
292     ftmp2 = ftmp * -1;
293
294     ftmp = bound(ftmp2, shortangle_f(player.v_angle_y - racer.angles_y, racer.angles_y), ftmp);
295     ftmp2 = anglemods(racer.angles_y + ftmp);
296
297     // Roll
298     ftmp = bound(-45, shortangle_f(player.v_angle_z + ((racer.angles_y - ftmp2) * autocvar_g_vehicle_racer_turnroll), racer.angles_z), 45);
299     ftmp = anglemods(racer.angles_z + ftmp) * frametime;
300     racer.angles_z = bound(-65, racer.angles_z + ftmp, 65);
301
302     // Turn
303     racer.angles_y = ftmp2;
304
305     // Pitch Body
306     ftmp = autocvar_g_vehicle_racer_pitchspeed * sys_frametime;
307     ftmp2 = ftmp * -1;
308
309     ftmp = bound(ftmp2, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp);
310     racer.angles_x = anglemods(racer.angles_x + ftmp);
311     racer.angles_x *= -1;
312
313 /*
314     crosshair_trace(player);
315     df = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
316     if(df_x > 180) df_x -= 360;
317     if(df_x < -180) df_x += 360;
318     if(df_y > 180) df_y -= 360;
319     if(df_y < -180) df_y += 360;
320
321     // Yaw
322     ftmp = autocvar_g_vehicle_racer_turnspeed * sys_frametime;
323     ftmp2 = ftmp * -1;
324
325     ftmp = bound(ftmp2, shortangle_f(df_y - racer.angles_y, racer.angles_y), ftmp);
326     ftmp2 = anglemods(racer.angles_y + ftmp);
327
328     // Roll
329     ftmp = bound(-45, shortangle_f(df_z + ((racer.angles_y - ftmp2) * autocvar_g_vehicle_racer_turnroll), racer.angles_z), 45);
330     ftmp = anglemods(racer.angles_z + ftmp) * frametime;
331     racer.angles_z = bound(-65, racer.angles_z + ftmp, 65);
332
333     // Turn
334     racer.angles_y = ftmp2;
335
336     // Pitch Body
337     ftmp = autocvar_g_vehicle_racer_pitchspeed * sys_frametime;
338     ftmp2 = ftmp * -1;
339
340     ftmp = bound(ftmp2, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp);
341     racer.angles_x = anglemods(racer.angles_x + ftmp);
342
343     racer.angles_x *= -1;
344     makevectors(racer.angles);
345     racer.angles_x *= -1;
346 */
347
348     df = racer.velocity * -0.5;
349
350     if(player.movement_x != 0)
351     {
352         if(player.movement_x > 0)
353             df += v_forward  * autocvar_g_vehicle_racer_speed_forward;
354         else if(player.movement_x < 0)
355             df -= v_forward  * autocvar_g_vehicle_racer_speed_forward;
356     }
357
358     if(player.movement_y != 0)
359     {
360         if(player.movement_y < 0)
361             df -= v_right * autocvar_g_vehicle_racer_speed_strafe;
362         else if(player.movement_y > 0)
363             df += v_right * autocvar_g_vehicle_racer_speed_strafe;
364     }
365
366     // limit _z to avoid flying on normal thrust
367     if(df_z > 0)
368         df_z = min(df_z, 700);
369
370     // Afterburn
371     if (player.BUTTON_JUMP)
372     if(racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * frametime))
373     {
374         racer.wait = time;
375         racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * frametime;
376         df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
377         self.owner.vehicle_energy = racer.vehicle_energy / autocvar_g_vehicle_racer_energy;
378     }
379
380     racer.velocity  += df * frametime;
381
382     df = (vlen(racer.velocity) * autocvar_g_vehicle_racer_downforce * v_up) * frametime;
383     racer.velocity  = racer.velocity - df;
384     player.movement = racer.velocity;
385
386     if(player.BUTTON_ATCK)
387     if(time > racer.attack_finished_single)
388     if(racer.vehicle_energy >= autocvar_g_vehicle_racer_laser_cost)
389     {
390         racer.vehicle_energy -= autocvar_g_vehicle_racer_laser_cost;
391         racer.wait = time;
392
393         crosshair_trace(player);
394         if(racer.cnt)
395         {
396             racer_fire_cannon("tag_fire1");
397             racer.cnt = 0;
398         }
399         else
400         {
401             racer_fire_cannon("tag_fire2");
402             racer.cnt = 1;
403         }
404         racer.attack_finished_single = time + autocvar_g_vehicle_racer_laser_refire;
405         self.owner.vehicle_energy = racer.vehicle_energy / autocvar_g_vehicle_racer_energy;
406     }
407
408     if(time > racer.delay)
409     {
410         if(player.BUTTON_ATCK2)
411         {
412             crosshair_trace(player);
413
414             if(!player.fire2_waspressed)
415             {
416                 if not (trace_ent)
417                 {
418                     player.BUTTON_ATCK2 = 0;
419                     player.fire2_waspressed = 1;
420                 }
421             }
422             else
423             {
424                 if(racer.phase < time)
425                 {
426                     if(self.lock_target && self.lock_strength == 1)
427                     {
428                         self.lock_strength = 0;
429                         self.lock_target = world;
430                     }
431
432                     vehicles_locktarget((1 / autocvar_g_vehicle_racer_rocket_locktime) * frametime);
433                     if(self.lock_target)
434                     {
435                         if(self.lock_strength == 1)
436                         {
437                             UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0);
438                             racer.phase = time + 5;
439                         }
440                         else if(self.lock_strength > 0.75)
441                             UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 1', 0);
442                         else if(self.lock_strength > 0.5)
443                             UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 1 0', 0);
444                         else if(self.lock_strength < 0.5)
445                             UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 0 1', 0);
446                     }
447                 }
448                 else
449                 {
450                     UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0);
451                 }
452             }
453         }
454
455         if(!player.BUTTON_ATCK2)
456         {
457             if(player.fire2_waspressed)
458             {
459                 racer_fire_rocket("tag_rocket_r", ((self.lock_strength == 1 && self.lock_target) ? self.lock_target : world));
460                 racer_fire_rocket("tag_rocket_l", ((self.lock_strength == 1 && self.lock_target) ? self.lock_target : world));
461
462                 self.lock_strength = 0;
463                 self.lock_target = world;
464
465                 racer.delay = time + autocvar_g_vehicle_racer_rocket_refire;
466                 racer.lip = time;
467             }
468         }
469     }
470
471     player.fire2_waspressed = player.BUTTON_ATCK2;
472
473     player.vehicle_reload1 = (time - racer.lip) / (racer.delay - racer.lip);
474
475     if(self.vehicle_flags  & VHF_SHIELDREGEN)
476         vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime);
477
478     if(self.vehicle_flags  & VHF_HEALTHREGEN)
479         vehicles_regen(dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime);
480
481     if(self.vehicle_flags  & VHF_ENERGYREGEN)
482         vehicles_regen(wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime);
483
484
485     VEHICLE_UPDATE_PLAYER(health, racer);
486
487     if(self.vehicle_flags & VHF_HASSHIELD)
488         VEHICLE_UPDATE_PLAYER(shield, racer);
489
490
491     player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
492     setorigin(player,racer.origin + '0 0 32');
493     player.velocity = racer.velocity;
494
495     self = player;
496     return 1;
497 }
498
499 void racer_think()
500 {
501     float a, b, c;
502
503     self.nextthink = time;
504
505     a = autocvar_g_vehicle_racer_anglestabilizer;
506     b = autocvar_g_vehicle_racer_springlength;
507     c = autocvar_g_vehicle_racer_hoverpower;
508
509     autocvar_g_vehicle_racer_anglestabilizer = 36;
510     autocvar_g_vehicle_racer_springlength = 96;
511     autocvar_g_vehicle_racer_hoverpower = 300;
512
513     racer_align4point();
514
515     if(self.velocity_z < 0)
516         self.velocity_z *= 0.95;
517
518     autocvar_g_vehicle_racer_anglestabilizer = a;
519     autocvar_g_vehicle_racer_springlength = b;
520     autocvar_g_vehicle_racer_hoverpower = c;
521
522     self.velocity_x *= 0.95;
523     self.velocity_y *= 0.95;
524 }
525
526 void racer_enter()
527 {
528     self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health);
529     self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield);
530 }
531
532 void racer_spawn();
533
534 void racer_exit(float eject)
535 {
536     vehicles_setreturn(autocvar_g_vehicle_racer_respawntime, racer_spawn);
537
538     self.think      = racer_think;
539     self.nextthink  = time;
540
541     if not (self.owner)
542         return;
543
544         if(eject)
545         {
546             makevectors(self.angles);
547             setorigin(self.owner,self.origin + v_forward * 100);
548             self.owner.velocity = (v_up + v_forward * 0.25) * 750;
549         }
550         else
551         {
552             self.owner.velocity = v_forward * -150;
553         setorigin(self.owner, self.origin - v_forward * 128);
554         }
555
556     self.owner = world;
557 }
558
559 void racer_touch()
560 {
561     if(self.owner)
562     {
563         if(vlen(self.velocity) == 0)
564             return;
565
566         if(other.classname != "player")
567             return;
568
569         vector a;
570         a = normalize(other.origin - self.origin);
571         a = a - normalize(self.velocity);
572
573         return;
574     }
575
576     if(other.classname != "player")
577         return;
578
579     if(other.deadflag != DEAD_NO)
580         return;
581
582     if(other.vehicle != world)
583         return;
584
585     vehicles_enter();
586 }
587
588 void racer_spawn()
589 {
590     self.think = racer_think;
591     self.nextthink = time;
592
593     self.flags      = FL_NOTARGET;
594     self.effects   = 0;
595
596
597     self.vehicle_health = autocvar_g_vehicle_racer_health;
598     self.vehicle_shield = autocvar_g_vehicle_racer_shield;
599
600     self.event_damage = vehicles_damage;
601     self.touch      = racer_touch;
602
603     self.iscreature = TRUE;
604     self.scale      = 0.5;
605     self.movetype   = MOVETYPE_BOUNCEMISSILE;
606     self.solid      = SOLID_SLIDEBOX;
607     self.takedamage = DAMAGE_AIM;
608
609     self.alpha      = 1;
610         self.colormap   = 1024;
611         self.deadflag   = DEAD_NO;
612     self.bot_attack = TRUE;
613
614     self.vehicle_energy = 1;
615     self.vehicle_hudmodel.viewmodelforclient = self;
616
617     setorigin(self, self.pos1);
618     self.angles = self.pos2;
619
620     setsize(self, RACER_MIN * 0.5, RACER_MAX * 0.5);
621     pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
622     self.delay = time;
623
624     vehicles_common_spawn();
625 }
626
627 void racer_blowup()
628 {
629
630     sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
631     pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
632
633     self.deadflag    = DEAD_DEAD;
634     self.vehicle_exit(VHEF_NORMAL);
635     RadiusDamage (self, self, 250, 15, 250, world, 250, DEATH_WAKIBLOWUP, world);
636
637     self.alpha = -1;
638     self.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
639     self.think      = racer_spawn;
640     self.movetype   = MOVETYPE_NONE;
641     self.effects    = EF_NODRAW;
642
643     self.avelocity_z  = 0;
644     self.colormod = '0 0 0';
645
646     setorigin(self,self.pos1);
647     self.avelocity = '0 0 0';
648     self.velocity = '0 0 0';
649 }
650
651 void racer_deadtouch()
652 {
653     self.avelocity_x *= 0.7;
654     self.cnt -= 1;
655     if(self.cnt <= 0)
656         racer_blowup();
657 }
658
659 void racer_die()
660 {
661     self.health       = 0;
662     self.event_damage = SUB_Null;
663     self.iscreature   = FALSE;
664     self.solid        = SOLID_CORPSE;
665     self.takedamage   = DAMAGE_NO;
666     self.deadflag     = DEAD_DYING;
667     self.movetype     = MOVETYPE_BOUNCE;
668     self.wait         = time;
669     self.cnt          = 1 + random() * 3;
670     self.touch        = racer_deadtouch;
671
672     pointparticles(particleeffectnum("rocket_explode"), findbetterlocation (self.origin, 16), '0 0 0', 1);
673
674     self.velocity     += '0 0 700';
675
676     if(random() < 0.5)
677         self.avelocity_z  = 16;
678     else
679         self.avelocity_z  = -16;
680
681     self.avelocity_x = -vlen(self.velocity) * 0.2;
682
683     self.colormod = '-0.5 -0.5 -0.5';
684
685         self.think     = racer_blowup;
686         self.nextthink = 2 + time + random() * 3;
687 }
688
689 void racer_dinit()
690 {
691     if not (vehicle_initialize(
692              "Wakizashi",
693              "models/vehicles/wakizashi.dpm",
694              "",
695              "models/vehicles/wakizashi_cockpit.dpm",
696              "", "", "tag_viewport",
697              HUD_WAKIZASHI,
698              0.5 * RACER_MIN, 0.5 * RACER_MAX,
699              FALSE,
700              racer_spawn, racer_frame,
701              racer_enter, racer_exit,
702              racer_die,   racer_think))
703     {
704         remove(self);
705         return;
706     }
707
708     if(autocvar_g_vehicle_racer_hovertype != 0)
709         racer_force_from_tag = vehicles_force_fromtag_maglev;
710     else
711         racer_force_from_tag = vehicles_force_fromtag_hover;
712
713     // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
714     self.scale = 0.5;
715     setattachment(self.vehicle_hudmodel, self, "");
716     setattachment(self.vehicle_viewport, self, "tag_viewport");
717 }
718
719 void spawnfunc_vehicle_racer()
720 {
721
722     precache_sound ("weapons/laserimpact.wav");
723     precache_sound ("weapons/lasergun_fire.wav");
724     precache_sound ("weapons/rocket_fire.wav");
725     precache_sound ("weapons/rocket_impact.wav");
726
727     precache_model ("models/vhshield.md3");
728     precache_model ("models/vehicles/wakizashi.dpm");
729     precache_model ("models/vehicles/wakizashi_cockpit.dpm");
730
731
732     vehicles_configcheck("vehicle_racer.cfg", autocvar_g_vehicle_racer_health);
733     if(autocvar_g_vehicle_racer_energy)
734         if(autocvar_g_vehicle_racer_energy_regen)
735             self.vehicle_flags |= VHF_ENERGYREGEN;
736
737     if(autocvar_g_vehicle_racer_shield)
738         self.vehicle_flags |= VHF_HASSHIELD;
739
740     if(autocvar_g_vehicle_racer_shield_regen)
741         self.vehicle_flags |= VHF_SHIELDREGEN;
742
743     if(autocvar_g_vehicle_racer_health_regen)
744         self.vehicle_flags |= VHF_HEALTHREGEN;
745
746     self.think = racer_dinit;
747     self.nextthink = time + 1;
748 }