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