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