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