ca3c76a9f7a0a6a4b3138cfedd6568e566dad021
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / unit / racer.qc
1 #ifdef REGISTER_VEHICLE
2 REGISTER_VEHICLE(
3 /* VEH_##id   */ RACER,
4 /* function   */ v_racer,
5 /* spawnflags */ VHF_DMGSHAKE | VHF_DMGROLL,
6 /* mins,maxs  */ '-120 -120 -40' * 0.5, '120 120 40' * 0.5,
7 /* model          */ "models/vehicles/wakizashi.dpm",
8 /* head_model */ "null",
9 /* hud_model  */ "models/vehicles/wakizashi_cockpit.dpm",
10 /* tags           */ "", "", "tag_viewport",
11 /* netname        */ "racer",
12 /* fullname   */ _("Racer")
13 );
14 #else
15 #ifdef SVQC
16 bool autocvar_g_vehicle_racer;
17
18 float autocvar_g_vehicle_racer_speed_afterburn;
19 float autocvar_g_vehicle_racer_afterburn_cost;
20
21 float autocvar_g_vehicle_racer_waterburn_cost;
22 float autocvar_g_vehicle_racer_waterburn_speed;
23
24 float autocvar_g_vehicle_racer_water_speed_forward;
25 float autocvar_g_vehicle_racer_water_speed_strafe;
26
27 float autocvar_g_vehicle_racer_anglestabilizer;
28 float autocvar_g_vehicle_racer_downforce;
29
30 float autocvar_g_vehicle_racer_speed_forward;
31 float autocvar_g_vehicle_racer_speed_strafe;
32 float autocvar_g_vehicle_racer_springlength;
33 float autocvar_g_vehicle_racer_upforcedamper;
34 float autocvar_g_vehicle_racer_friction;
35
36 float autocvar_g_vehicle_racer_water_time = 5;
37
38 float autocvar_g_vehicle_racer_hovertype;
39 float autocvar_g_vehicle_racer_hoverpower;
40
41 float autocvar_g_vehicle_racer_turnroll;
42 float autocvar_g_vehicle_racer_turnspeed;
43 float autocvar_g_vehicle_racer_pitchspeed;
44
45 float autocvar_g_vehicle_racer_energy;
46 float autocvar_g_vehicle_racer_energy_regen;
47 float autocvar_g_vehicle_racer_energy_regen_pause;
48
49 float autocvar_g_vehicle_racer_health;
50 float autocvar_g_vehicle_racer_health_regen;
51 float autocvar_g_vehicle_racer_health_regen_pause;
52
53 float autocvar_g_vehicle_racer_shield;
54 float autocvar_g_vehicle_racer_shield_regen;
55 float autocvar_g_vehicle_racer_shield_regen_pause;
56
57 float autocvar_g_vehicle_racer_cannon_cost;
58 float autocvar_g_vehicle_racer_cannon_damage;
59 float autocvar_g_vehicle_racer_cannon_radius;
60 float autocvar_g_vehicle_racer_cannon_refire;
61 float autocvar_g_vehicle_racer_cannon_speed;
62 float autocvar_g_vehicle_racer_cannon_spread;
63 float autocvar_g_vehicle_racer_cannon_force;
64
65 float autocvar_g_vehicle_racer_rocket_accel;
66 float autocvar_g_vehicle_racer_rocket_damage;
67 float autocvar_g_vehicle_racer_rocket_radius;
68 float autocvar_g_vehicle_racer_rocket_force;
69 float autocvar_g_vehicle_racer_rocket_refire;
70 float autocvar_g_vehicle_racer_rocket_speed;
71 float autocvar_g_vehicle_racer_rocket_turnrate;
72
73 float autocvar_g_vehicle_racer_rocket_locktarget;
74 float autocvar_g_vehicle_racer_rocket_locking_time;
75 float autocvar_g_vehicle_racer_rocket_locking_releasetime;
76 float autocvar_g_vehicle_racer_rocket_locked_time;
77 float autocvar_g_vehicle_racer_rocket_locked_maxangle;
78 float autocvar_g_vehicle_racer_rocket_climbspeed;
79
80 float autocvar_g_vehicle_racer_respawntime;
81
82 float autocvar_g_vehicle_racer_blowup_radius;
83 float autocvar_g_vehicle_racer_blowup_coredamage;
84 float autocvar_g_vehicle_racer_blowup_edgedamage;
85 float autocvar_g_vehicle_racer_blowup_forceintensity;
86
87 float autocvar_g_vehicle_racer_bouncefactor;
88 float autocvar_g_vehicle_racer_bouncestop;
89 vector autocvar_g_vehicle_racer_bouncepain;
90
91 var vector racer_force_from_tag(string tag_name, float spring_length, float max_power);
92
93 void racer_align4point(float _delta)
94 {
95         vector push_vector;
96         float fl_push, fr_push, bl_push, br_push;
97
98         push_vector  = racer_force_from_tag("tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
99         fr_push   = force_fromtag_normpower;
100         //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
101
102         push_vector += racer_force_from_tag("tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
103         fl_push   = force_fromtag_normpower;
104         //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
105
106         push_vector += racer_force_from_tag("tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
107         br_push   = force_fromtag_normpower;
108         //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
109
110         push_vector += racer_force_from_tag("tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
111         bl_push   = force_fromtag_normpower;
112         //vehicles_sweap_collision(force_fromtag_origin, self.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
113
114         self.velocity += push_vector * _delta;
115         
116         if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER)
117         if(self.owner.BUTTON_CROUCH && time < self.air_finished)
118                 self.velocity_z += 30;
119         else
120                 self.velocity_z += 200;
121
122         // Anti ocilation
123         if(self.velocity_z > 0)
124                 self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * _delta;
125
126         push_vector_x =  (fl_push - bl_push);
127         push_vector_x += (fr_push - br_push);
128         push_vector_x *= 360;
129
130         push_vector_z = (fr_push - fl_push);
131         push_vector_z += (br_push - bl_push);
132         push_vector_z *= 360;
133
134         // Apply angle diffrance
135         self.angles_z += push_vector_z * _delta;
136         self.angles_x += push_vector_x * _delta;
137
138         // Apply stabilizer
139         self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
140         self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
141 }
142
143 void racer_fire_cannon(string tagname)
144 {
145         vector v;
146         entity bolt;
147
148         v = gettaginfo(self, gettagindex(self, tagname));
149         bolt = vehicles_projectile("wakizashi_gun_muzzleflash", "weapons/lasergun_fire.wav",
150                                                    v, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
151                                                    autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force,  0,
152                                                    DEATH_VH_WAKI_GUN, PROJECTILE_WAKICANNON, 0, true, true, self.owner);
153
154         // Fix z-aim (for chase mode)
155         v = normalize(trace_endpos - bolt.origin);
156         v_forward_z = v_z * 0.5;
157         bolt.velocity = v_forward * autocvar_g_vehicle_racer_cannon_speed;
158 }
159
160 void racer_rocket_groundhugger()
161 {
162         vector olddir, newdir;
163         float oldvel, newvel;
164
165         self.nextthink  = time;
166
167         if(self.owner.deadflag != DEAD_NO || self.cnt < time)
168         {
169                 self.use();
170                 return;
171         }
172
173         if(!self.realowner.vehicle)
174         {
175                 UpdateCSQCProjectile(self);
176                 return;
177         }
178
179         olddir = normalize(self.velocity);
180         oldvel = vlen(self.velocity);
181         newvel = oldvel + self.lip;
182
183         tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self);
184         if(trace_fraction <= 0.5)
185         {
186                 // Hitting somethign soon, just speed ahead
187                 self.velocity = olddir * newvel;
188                 UpdateCSQCProjectile(self);
189                 return;
190         }
191
192         traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self);
193         if(trace_fraction != 1.0)
194         {
195                 newdir = normalize(trace_endpos + '0 0 64' - self.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
196                 self.velocity = normalize(olddir + newdir) * newvel;
197         }
198         else
199         {
200                 self.velocity = olddir * newvel;
201                 self.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
202         }
203         
204         if(pointcontents(self.origin - '0 0 32') == CONTENT_WATER)
205                 self.velocity_z += 200;
206
207         UpdateCSQCProjectile(self);
208         return;
209 }
210
211 void racer_rocket_tracker()
212 {
213         vector olddir, newdir;
214         float oldvel, newvel;
215
216         self.nextthink  = time;
217
218         if (self.owner.deadflag != DEAD_NO || self.cnt < time)
219         {
220                 self.use();
221                 return;
222         }
223
224         if(!self.realowner.vehicle)
225         {
226                 UpdateCSQCProjectile(self);
227                 return;
228         }
229
230         olddir = normalize(self.velocity);
231         oldvel = vlen(self.velocity);
232         newvel = oldvel + self.lip;
233         makevectors(vectoangles(olddir));
234
235         float time_to_impact = min(vlen(self.enemy.origin - self.origin) / vlen(self.velocity), 1);
236         vector predicted_origin = self.enemy.origin + self.enemy.velocity * time_to_impact;
237
238         traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self);
239         newdir = normalize(predicted_origin - self.origin);
240
241         //vector
242         float height_diff = predicted_origin_z - self.origin_z;
243
244         if(vlen(newdir - v_forward) > autocvar_g_vehicle_racer_rocket_locked_maxangle)
245         {
246                 //bprint("Target lost!\n");
247                 //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
248                 self.think = racer_rocket_groundhugger;
249                 return;
250         }
251
252         if(trace_fraction != 1.0 && trace_ent != self.enemy)
253                 newdir_z += 16 * sys_frametime;
254
255         self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
256         self.velocity_z -= 800 * sys_frametime;
257         self.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ;
258
259         UpdateCSQCProjectile(self);
260         return;
261 }
262
263 void racer_fire_rocket(string tagname, entity trg)
264 {
265         vector v = gettaginfo(self, gettagindex(self, tagname));
266         entity rocket = rocket = vehicles_projectile("wakizashi_rocket_launch", "weapons/rocket_fire.wav",
267                                                    v, v_forward * autocvar_g_vehicle_racer_rocket_speed,
268                                                    autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
269                                                    DEATH_VH_WAKI_ROCKET, PROJECTILE_WAKIROCKET, 20, false, false, self.owner);
270
271         rocket.lip                        = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
272         rocket.wait                      = autocvar_g_vehicle_racer_rocket_turnrate;
273         rocket.nextthink                = time;
274         rocket.enemy                    = trg;
275         rocket.cnt                        = time + 15;
276
277         if(trg)
278                 rocket.think                    = racer_rocket_tracker;
279         else
280                 rocket.think                    = racer_rocket_groundhugger;
281 }
282
283 float racer_frame()
284 {
285         entity player, racer;
286         vector df;
287         float ftmp;
288
289         if(intermission_running)
290         {
291                 self.vehicle.velocity = '0 0 0';
292                 self.vehicle.avelocity = '0 0 0';
293                 return 1;
294         }
295
296         player  = self;
297         racer   = self.vehicle;
298         self    = racer;
299
300         vehicles_painframe();
301
302         if(pointcontents(racer.origin) != CONTENT_WATER)
303                 racer.air_finished = time + autocvar_g_vehicle_racer_water_time;
304
305         if(racer.deadflag != DEAD_NO)
306         {
307                 self = player;
308                 player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
309                 return 1;
310         }
311
312         racer_align4point(frametime);
313
314         player.BUTTON_ZOOM = player.BUTTON_CROUCH = 0;
315
316         crosshair_trace(player);
317
318         racer.angles_x *= -1;
319
320         // Yaw
321         ftmp = autocvar_g_vehicle_racer_turnspeed * frametime;
322         ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - racer.angles_y, racer.angles_y), ftmp);
323         racer.angles_y = anglemods(racer.angles_y + ftmp);
324
325         // Roll
326         racer.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * frametime;
327
328         // Pitch
329         ftmp = autocvar_g_vehicle_racer_pitchspeed  * frametime;
330         ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp);
331         racer.angles_x = bound(-30, anglemods(racer.angles_x + ftmp), 30);
332
333         makevectors(racer.angles);
334         racer.angles_x *= -1;
335
336         //ftmp = racer.velocity_z;
337         df = racer.velocity * -autocvar_g_vehicle_racer_friction;
338         //racer.velocity_z = ftmp;
339
340         if(vlen(player.movement) != 0)
341         {
342                 if(pointcontents(racer.origin) == CONTENT_WATER)
343                 {
344                         if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); }
345                         if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); }
346                 }
347                 else
348                 {
349                         if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); }
350                         if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); }
351                 }
352
353                 if(self.sound_nexttime < time || self.sounds != 1)
354                 {
355                         self.sounds = 1;
356                         self.sound_nexttime = time + 10.922667; //soundlength("vehicles/racer_move.wav");
357                         sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_move.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
358                 }
359         }
360         else
361         {
362                 if(self.sound_nexttime < time || self.sounds != 0)
363                 {
364                         self.sounds = 0;
365                         self.sound_nexttime = time + 11.888604; //soundlength("vehicles/racer_idle.wav");
366                         sound (self, CH_TRIGGER_SINGLE, "vehicles/racer_idle.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
367                 }
368         }
369
370         // Afterburn
371         if (player.BUTTON_JUMP && racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * frametime))
372         {
373                 if(time - racer.wait > 0.2)
374                         pointparticles(particleeffectnum("wakizashi_booster_smoke"), self.origin - v_forward * 32, v_forward  * vlen(self.velocity), 1);
375
376                 racer.wait = time;
377
378                 if(pointcontents(racer.origin) == CONTENT_WATER)
379                 {
380                         racer.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * frametime;
381                         df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
382                 }
383                 else
384                 {
385                         racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * frametime;
386                         df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
387                 }
388
389                 if(racer.invincible_finished < time)
390                 {
391                         traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self);
392                         if(trace_fraction != 1.0)
393                                 pointparticles(particleeffectnum("smoke_small"), trace_endpos, '0 0 0', 1);
394
395                         racer.invincible_finished = time + 0.1 + (random() * 0.1);
396                 }
397
398                 if(racer.strength_finished < time)
399                 {
400                         racer.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav");
401                         sound (racer.tur_head, CH_TRIGGER_SINGLE, "vehicles/racer_boost.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
402                 }
403         }
404         else
405         {
406                 racer.strength_finished = 0;
407                 sound (racer.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
408         }
409
410         df -= v_up * (vlen(racer.velocity) * autocvar_g_vehicle_racer_downforce);
411         player.movement = racer.velocity += df * frametime;
412
413         if(!forbidWeaponUse(player))
414         if(player.BUTTON_ATCK)
415         if(time > racer.attack_finished_single)
416         if(racer.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost)
417         {
418                 racer.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
419                 racer.wait = time;
420
421                 crosshair_trace(player);
422                 if(racer.cnt)
423                 {
424                         racer_fire_cannon("tag_fire1");
425                         racer.cnt = 0;
426                 }
427                 else
428                 {
429                         racer_fire_cannon("tag_fire2");
430                         racer.cnt = 1;
431                 }
432                 racer.attack_finished_single = time + autocvar_g_vehicle_racer_cannon_refire;
433         }
434
435         if(autocvar_g_vehicle_racer_rocket_locktarget)
436         {
437                 vehicles_locktarget((1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
438                                                  (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
439                                                  autocvar_g_vehicle_racer_rocket_locked_time);
440
441                 if(self.lock_target)
442                 {
443                         if(racer.lock_strength == 1)
444                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0);
445                         else if(self.lock_strength > 0.5)
446                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 1 0', 0);
447                         else if(self.lock_strength < 0.5)
448                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 0 1', 0);
449                 }
450         }
451
452         if(!forbidWeaponUse(player))
453         if(time > racer.delay)
454         if(player.BUTTON_ATCK2)
455         {
456                 racer.misc_bulletcounter += 1;
457                 racer.delay = time + 0.3;
458
459                 if(racer.misc_bulletcounter == 1)
460                         racer_fire_rocket("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
461                 else if(racer.misc_bulletcounter == 2)
462                 {
463                         racer_fire_rocket("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
464                         racer.lock_strength  = 0;
465                         racer.lock_target       = world;
466                         racer.misc_bulletcounter = 0;
467
468                         racer.delay = time + autocvar_g_vehicle_racer_rocket_refire;
469                         racer.lip = time;
470                 }
471         }
472         player.vehicle_reload1 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100);
473
474         if(racer.vehicle_flags  & VHF_SHIELDREGEN)
475                 vehicles_regen(racer.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, true);
476
477         if(racer.vehicle_flags  & VHF_HEALTHREGEN)
478                 vehicles_regen(racer.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, false);
479
480         if(racer.vehicle_flags  & VHF_ENERGYREGEN)
481                 vehicles_regen(racer.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, false);
482
483
484         VEHICLE_UPDATE_PLAYER(player, health, racer);
485         VEHICLE_UPDATE_PLAYER(player, energy, racer);
486
487         if(racer.vehicle_flags & VHF_HASSHIELD)
488                 VEHICLE_UPDATE_PLAYER(player, shield, racer);
489
490         player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
491         setorigin(player,racer.origin + '0 0 32');
492         player.velocity = racer.velocity;
493
494         self = player;
495         return 1;
496 }
497
498 void racer_think()
499 {
500         self.nextthink = time;
501
502         float pushdeltatime = time - self.lastpushtime;
503         if (pushdeltatime > 0.15) pushdeltatime = 0;
504         self.lastpushtime = time;
505         if(!pushdeltatime) return;
506
507         tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, self);
508
509         vector df = self.velocity * -autocvar_g_vehicle_racer_friction;
510         df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2);
511
512         if(pointcontents(self.origin - '0 0 64') == CONTENT_WATER)
513                 self.velocity_z += 200;
514
515         self.velocity += df * pushdeltatime;
516         if(self.velocity_z > 0)
517                 self.velocity_z *= 1 - autocvar_g_vehicle_racer_upforcedamper * pushdeltatime;
518
519         self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
520         self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
521         
522         CSQCMODEL_AUTOUPDATE();
523 }
524
525 void racer_exit(float eject)
526 {
527         vector spot;
528
529         self.think        = racer_think;
530         self.nextthink  = time;
531         self.movetype   = MOVETYPE_BOUNCE;
532         sound (self.tur_head, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
533
534         if(!self.owner)
535                 return;
536
537         makevectors(self.angles);
538         if(eject)
539         {
540                 spot = self.origin + v_forward * 100 + '0 0 64';
541                 spot = vehicles_findgoodexit(spot);
542                 setorigin(self.owner , spot);
543                 self.owner.velocity = (v_up + v_forward * 0.25) * 750;
544                 self.owner.oldvelocity = self.owner.velocity;
545         }
546         else
547         {
548                 if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
549                 {
550                         self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
551                         self.owner.velocity_z += 200;
552                         spot = self.origin + v_forward * 32 + '0 0 32';
553                         spot = vehicles_findgoodexit(spot);
554                 }
555                 else
556                 {
557                         self.owner.velocity = self.velocity * 0.5;
558                         self.owner.velocity_z += 10;
559                         spot = self.origin - v_forward * 200 + '0 0 32';
560                         spot = vehicles_findgoodexit(spot);
561                 }
562                 self.owner.oldvelocity = self.owner.velocity;
563                 setorigin(self.owner , spot);
564         }
565         antilag_clear(self.owner);
566         self.owner = world;
567 }
568
569 void racer_blowup()
570 {
571         self.deadflag   = DEAD_DEAD;
572         self.vehicle_exit(VHEF_NORMAL);
573
574         RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage,
575                                         autocvar_g_vehicle_racer_blowup_edgedamage,
576                                         autocvar_g_vehicle_racer_blowup_radius, world, world,
577                                         autocvar_g_vehicle_racer_blowup_forceintensity,
578                                         DEATH_VH_WAKI_DEATH, world);
579
580         self.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
581         self.think        = vehicles_spawn;
582         self.movetype   = MOVETYPE_NONE;
583         self.effects    = EF_NODRAW;
584
585         self.colormod  = '0 0 0';
586         self.avelocity = '0 0 0';
587         self.velocity  = '0 0 0';
588
589         setorigin(self, self.pos1);
590 }
591
592 void racer_blowup_think()
593 {
594         self.nextthink = time;
595         
596         if(time >= self.delay)
597                 racer_blowup();
598         
599         CSQCMODEL_AUTOUPDATE();
600 }
601
602 void racer_deadtouch()
603 {
604         self.avelocity_x *= 0.7;
605         self.cnt -= 1;
606         if(self.cnt <= 0)
607                 racer_blowup();
608 }
609
610 void spawnfunc_vehicle_racer()
611 {
612         if(!autocvar_g_vehicle_racer) { remove(self); return; }
613         if(!vehicle_initialize(VEH_RACER, false)) { remove(self); return; }
614 }
615
616 float v_racer(float req)
617 {
618         switch(req)
619         {
620                 case VR_IMPACT:
621                 {
622                         if(autocvar_g_vehicle_racer_bouncepain)
623                                 vehicles_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z);
624                         return true;
625                 }
626                 case VR_ENTER:
627                 {
628                         self.movetype = MOVETYPE_BOUNCE;
629                         self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health)  * 100;
630                         self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield)  * 100;
631
632                         if(self.owner.flagcarried)
633                            setorigin(self.owner.flagcarried, '-190 0 96');
634                            
635                         return true;
636                 }
637                 case VR_THINK:
638                 {
639                         return true;
640                 }
641                 case VR_DEATH:
642                 {
643                         self.health                     = 0;
644                         self.event_damage       = func_null;
645                         self.solid                      = SOLID_CORPSE;
646                         self.takedamage         = DAMAGE_NO;
647                         self.deadflag           = DEAD_DYING;
648                         self.movetype           = MOVETYPE_BOUNCE;
649                         self.wait                       = time;
650                         self.delay                      = 2 + time + random() * 3;
651                         self.cnt                        = 1 + random() * 2;
652                         self.touch                      = racer_deadtouch;
653
654                         pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
655
656                         if(random() < 0.5)
657                                 self.avelocity_z = 32;
658                         else
659                                 self.avelocity_z = -32;
660
661                         self.avelocity_x = -vlen(self.velocity) * 0.2;
662                         self.velocity += '0 0 700';
663                         self.colormod = '-0.5 -0.5 -0.5';
664
665                         self.think = racer_blowup_think;
666                         self.nextthink = time;
667         
668                         return true;
669                 }
670                 case VR_SPAWN:
671                 {
672                         if(self.scale != 0.5)
673                         {
674                                 if(autocvar_g_vehicle_racer_hovertype != 0)
675                                         racer_force_from_tag = vehicles_force_fromtag_maglev;
676                                 else
677                                         racer_force_from_tag = vehicles_force_fromtag_hover;
678
679                                 // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
680                                 self.scale = 0.5;
681                                 setattachment(self.vehicle_hudmodel, self, "");
682                                 setattachment(self.vehicle_viewport, self, "tag_viewport");
683
684                                 self.mass                          = 900;
685                         }
686
687                         self.think                = racer_think;
688                         self.nextthink    = time;
689                         self.vehicle_health = autocvar_g_vehicle_racer_health;
690                         self.vehicle_shield = autocvar_g_vehicle_racer_shield;
691
692                         self.movetype     = MOVETYPE_TOSS;
693                         self.solid                = SOLID_SLIDEBOX;
694                         self.delay                = time;
695                         self.scale                = 0.5;
696                         
697                         self.PlayerPhysplug = racer_frame;
698                         
699                         self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
700                         self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
701                         self.damageforcescale = 0.5;
702                         self.vehicle_health = autocvar_g_vehicle_racer_health;
703                         self.vehicle_shield = autocvar_g_vehicle_racer_shield;
704                         
705                         return true;
706                 }
707                 case VR_SETUP:
708                 {
709                         if(autocvar_g_vehicle_racer_energy)
710                         if(autocvar_g_vehicle_racer_energy_regen)
711                                 self.vehicle_flags |= VHF_ENERGYREGEN;
712
713                         if(autocvar_g_vehicle_racer_shield)
714                                 self.vehicle_flags |= VHF_HASSHIELD;
715
716                         if(autocvar_g_vehicle_racer_shield_regen)
717                                 self.vehicle_flags |= VHF_SHIELDREGEN;
718
719                         if(autocvar_g_vehicle_racer_health_regen)
720                                 self.vehicle_flags |= VHF_HEALTHREGEN;
721                                 
722                         self.vehicle_exit = racer_exit;
723                         self.respawntime = autocvar_g_vehicle_racer_respawntime;
724                         self.vehicle_health = autocvar_g_vehicle_racer_health;
725                         self.vehicle_shield = autocvar_g_vehicle_racer_shield;
726                         self.max_health = self.vehicle_health;
727                                 
728                         return true;
729                 }
730                 case VR_PRECACHE:
731                 {
732                         precache_sound ("weapons/lasergun_fire.wav");
733                         precache_sound ("weapons/rocket_fire.wav");
734
735                         precache_sound ("vehicles/racer_idle.wav");
736                         precache_sound ("vehicles/racer_move.wav");
737                         precache_sound ("vehicles/racer_boost.wav");
738
739                         precache_model ("models/vhshield.md3");
740                         precache_model ("models/vehicles/wakizashi.dpm");
741                         precache_model ("models/vehicles/wakizashi_cockpit.dpm");
742                         return true;
743                 }
744         }
745
746         return true;
747 }
748
749 #endif // SVQC
750 #ifdef CSQC
751
752 #define waki_ico "gfx/vehicles/waki.tga"
753 #define waki_eng "gfx/vehicles/waki_e.tga"
754 #define waki_gun "gfx/vehicles/waki_guns.tga"
755 #define waki_rkt "gfx/vehicles/waki_rockets.tga"
756 #define waki_xhair "gfx/vehicles/axh-special1.tga"
757
758 float v_racer(float req)
759 {
760         switch(req)
761         {
762                 case VR_HUD:
763                 {
764                         if(autocvar_r_letterbox)
765                                 return true;
766
767                         vector picsize, hudloc = '0 0 0', pic2size, picloc;
768
769                         // Fetch health & ammo stats
770                         HUD_GETVEHICLESTATS
771
772                         picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale;
773                         hudloc_y = vid_conheight - picsize_y;
774                         hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5;
775
776                         drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL);
777
778                         shield  *= 0.01;
779                         vh_health  *= 0.01;
780                         energy  *= 0.01;
781                         reload1 *= 0.01;
782
783                         pic2size = draw_getimagesize(waki_ico) * (autocvar_cl_vehicles_hudscale * 0.8);
784                         picloc = picsize * 0.5 - pic2size * 0.5;
785                         if(vh_health < 0.25)
786                                 drawpic(hudloc + picloc, waki_ico, pic2size,  '1 0 0' + '0 1 1' * sin(time * 8),  1, DRAWFLAG_NORMAL);
787                         else
788                                 drawpic(hudloc + picloc, waki_ico, pic2size,  '1 1 1' * vh_health  + '1 0 0' * (1 - vh_health),  1, DRAWFLAG_NORMAL);
789                         drawpic(hudloc + picloc, waki_eng, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
790                         drawpic(hudloc + picloc, waki_gun, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
791                         drawpic(hudloc + picloc, waki_rkt, pic2size,  '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL);
792                         drawpic(hudloc + picloc, hud_sh, pic2size,  '1 1 1', shield, DRAWFLAG_NORMAL);
793
794                 // Health bar
795                         picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale;
796                         picloc = '69 69 0' * autocvar_cl_vehicles_hudscale;
797                         drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight);
798                         drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL);
799                         drawresetcliparea();
800                 // ..  and icon
801                         picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale;
802                         picloc = '37 65 0' * autocvar_cl_vehicles_hudscale;
803                         if(vh_health < 0.25)
804                         {
805                                 if(alarm1time < time)
806                                 {
807                                         alarm1time = time + 2;
808                                         vehicle_alarm(self, CH_PAIN_SINGLE, "vehicles/alarm.wav");
809                                 }
810
811                                 drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
812                         }
813                         else
814                         {
815                                 drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
816                                 if(alarm1time)
817                                 {
818                                         vehicle_alarm(self, CH_PAIN_SINGLE, "misc/null.wav");
819                                         alarm1time = 0;
820                                 }
821                         }
822
823
824                 // Shield bar
825                         picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale;
826                         picloc = '69 140 0' * autocvar_cl_vehicles_hudscale;
827                         drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight);
828                         drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
829                         drawresetcliparea();
830                 // ..  and icon
831                         picloc = '40 136 0' * autocvar_cl_vehicles_hudscale;
832                         picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale;
833                         if(shield < 0.25)
834                         {
835                                 if(alarm2time < time)
836                                 {
837                                         alarm2time = time + 1;
838                                         vehicle_alarm(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav");
839                                 }
840                                 drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
841                         }
842                         else
843                         {
844                                 drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
845                                 if(alarm2time)
846                                 {
847                                         vehicle_alarm(self, CH_TRIGGER_SINGLE, "misc/null.wav");
848                                         alarm2time = 0;
849                                 }
850                         }
851
852                 // Gun bar
853                         picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale;
854                         picloc = '450 69 0' * autocvar_cl_vehicles_hudscale;
855                         drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight);
856                         drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
857                         drawresetcliparea();
858                 // ..  and icon
859                         picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale;
860                         picloc = '664 60 0' * autocvar_cl_vehicles_hudscale;
861                         if(energy < 0.2)
862                                 drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
863                         else
864                                 drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
865
866                 // Bomb bar
867                         picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale;
868                         picloc = '450 140 0' * autocvar_cl_vehicles_hudscale;
869                         drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight);
870                         drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
871                         drawresetcliparea();
872                 // ..  and icon
873                         pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale;
874                         picloc = '664 130 0' * autocvar_cl_vehicles_hudscale;
875                         if(reload1 != 1)
876                                 drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
877                         else
878                                 drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL);
879
880                         if (scoreboard_showscores)
881                                 HUD_DrawScoreboard();
882                         else
883                         {
884                                 picsize = draw_getimagesize(waki_xhair);
885                                 picsize_x *= 0.5;
886                                 picsize_y *= 0.5;
887
888
889                                 drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
890                         }
891                         return true;
892                 }
893                 case VR_SETUP:
894                 {
895                         AuxiliaryXhair[0].axh_image = "gfx/vehicles/axh-bracket.tga";
896                         AuxiliaryXhair[0].axh_scale = 0.25;
897                         return true;
898                 }
899                 case VR_PRECACHE:
900                 {
901                         return true;
902                 }
903         }
904
905         return true;
906 }
907
908 #endif // CSQC
909 #endif // REGISTER_VEHICLE