]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/vehicles/vehicle/racer.qc
6d0a2b9785657648071a65725afa1e3e9fc0ac3d
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / vehicle / racer.qc
1 #ifndef VEHICLE_RACER
2 #define VEHICLE_RACER
3
4 #include "racer_weapon.qc"
5
6 CLASS(Racer, Vehicle)
7 /* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
8 /* mins       */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5);
9 /* maxs       */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5);
10 /* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50');
11 /* view dist  */ ATTRIB(Racer, height, float, 200);
12 /* model          */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm");
13 /* model          */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm");
14 /* head_model */ ATTRIB(Racer, head_model, string, "null");
15 /* hud_model  */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm");
16 /* tags       */ ATTRIB(Racer, tag_head, string, "");
17 /* tags       */ ATTRIB(Racer, tag_hud, string, "");
18 /* tags       */ ATTRIB(Racer, tag_view, string, "tag_viewport");
19 /* netname    */ ATTRIB(Racer, netname, string, "racer");
20 /* fullname   */ ATTRIB(Racer, vehicle_name, string, _("Racer"));
21 /* icon       */ ATTRIB(Racer, m_icon, string, "vehicle_racer");
22 ENDCLASS(Racer)
23 REGISTER_VEHICLE(RACER, NEW(Racer));
24
25 #endif
26
27 #ifdef IMPLEMENTATION
28
29 #include "racer_weapon.qc"
30
31 #ifdef SVQC
32 #include <common/triggers/trigger/impulse.qh>
33
34 bool autocvar_g_vehicle_racer = true;
35
36 float autocvar_g_vehicle_racer_speed_afterburn = 3000;
37 // energy consumed per second
38 float autocvar_g_vehicle_racer_afterburn_cost = 100;
39
40 float autocvar_g_vehicle_racer_waterburn_cost = 5;
41 float autocvar_g_vehicle_racer_waterburn_speed = 750;
42
43 float autocvar_g_vehicle_racer_water_speed_forward = 600;
44 float autocvar_g_vehicle_racer_water_speed_strafe = 600;
45
46 float autocvar_g_vehicle_racer_pitchlimit = 30;
47
48 float autocvar_g_vehicle_racer_water_downforce = 0.03;
49 float autocvar_g_vehicle_racer_water_upforcedamper = 15;
50
51 float autocvar_g_vehicle_racer_anglestabilizer = 1.75;
52 float autocvar_g_vehicle_racer_downforce = 0.01;
53
54 float autocvar_g_vehicle_racer_speed_forward = 650;
55 float autocvar_g_vehicle_racer_speed_strafe = 650;
56 float autocvar_g_vehicle_racer_springlength = 70;
57 float autocvar_g_vehicle_racer_upforcedamper = 10;
58 float autocvar_g_vehicle_racer_friction = 0.45;
59
60 float autocvar_g_vehicle_racer_water_time = 5;
61
62 float autocvar_g_vehicle_racer_collision_multiplier = 0.05;
63
64 // 0 = hover, != 0 = maglev
65 int autocvar_g_vehicle_racer_hovertype = 0;
66 // NOTE!! x 4 (4 engines)
67 float autocvar_g_vehicle_racer_hoverpower = 8000;
68
69 float autocvar_g_vehicle_racer_turnroll = 30;
70 float autocvar_g_vehicle_racer_turnspeed = 220;
71 float autocvar_g_vehicle_racer_pitchspeed = 125;
72
73 float autocvar_g_vehicle_racer_energy = 100;
74 float autocvar_g_vehicle_racer_energy_regen = 50;
75 float autocvar_g_vehicle_racer_energy_regen_pause = 1;
76
77 float autocvar_g_vehicle_racer_health = 200;
78 float autocvar_g_vehicle_racer_health_regen = 0;
79 float autocvar_g_vehicle_racer_health_regen_pause = 0;
80
81 float autocvar_g_vehicle_racer_shield = 100;
82 float autocvar_g_vehicle_racer_shield_regen = 30;
83 float autocvar_g_vehicle_racer_shield_regen_pause = 1;
84
85 bool autocvar_g_vehicle_racer_rocket_locktarget = true;
86 float autocvar_g_vehicle_racer_rocket_locking_time = 0.9;
87 float autocvar_g_vehicle_racer_rocket_locking_releasetime = 0.5;
88 float autocvar_g_vehicle_racer_rocket_locked_time = 4;
89
90 float autocvar_g_vehicle_racer_respawntime = 35;
91
92 float autocvar_g_vehicle_racer_blowup_radius = 250;
93 float autocvar_g_vehicle_racer_blowup_coredamage = 250;
94 float autocvar_g_vehicle_racer_blowup_edgedamage = 15;
95 float autocvar_g_vehicle_racer_blowup_forceintensity = 250;
96
97 // Factor of old velocity to keep after collision
98 float autocvar_g_vehicle_racer_bouncefactor = 0.25;
99 // if != 0, New veloctiy after bounce = 0 if new velocity < this
100 float autocvar_g_vehicle_racer_bouncestop = 0;
101 // "minspeed_for_pain speedchange_to_pain_factor max_damage"
102 vector autocvar_g_vehicle_racer_bouncepain = '60 0.75 300';
103
104 .float racer_watertime;
105
106 var vector racer_force_from_tag(entity this, string tag_name, float spring_length, float max_power);
107
108 void racer_align4point(entity this, float _delta)
109 {
110         vector push_vector;
111         float fl_push, fr_push, bl_push, br_push;
112
113         push_vector  = racer_force_from_tag(this, "tag_engine_fr", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
114         fr_push   = force_fromtag_normpower;
115         //vehicles_sweap_collision(force_fromtag_origin, this.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
116
117         push_vector += racer_force_from_tag(this, "tag_engine_fl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
118         fl_push   = force_fromtag_normpower;
119         //vehicles_sweap_collision(force_fromtag_origin, this.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
120
121         push_vector += racer_force_from_tag(this, "tag_engine_br", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
122         br_push   = force_fromtag_normpower;
123         //vehicles_sweap_collision(force_fromtag_origin, this.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
124
125         push_vector += racer_force_from_tag(this, "tag_engine_bl", autocvar_g_vehicle_racer_springlength, autocvar_g_vehicle_racer_hoverpower);
126         bl_push   = force_fromtag_normpower;
127         //vehicles_sweap_collision(force_fromtag_origin, this.velocity, _delta, v_add, autocvar_g_vehicle_racer_collision_multiplier);
128
129         this.velocity += push_vector * _delta;
130
131         float uforce = autocvar_g_vehicle_racer_upforcedamper;
132
133         int cont = pointcontents(this.origin - '0 0 64');
134         if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
135         {
136                 uforce = autocvar_g_vehicle_racer_water_upforcedamper;
137
138                 if(PHYS_INPUT_BUTTON_CROUCH(this.owner) && time < this.air_finished)
139                         this.velocity_z += 30;
140                 else
141                         this.velocity_z += 200;
142         }
143
144
145         // Anti ocilation
146         if(this.velocity_z > 0)
147                 this.velocity_z *= 1 - uforce * _delta;
148
149         push_vector_x =  (fl_push - bl_push);
150         push_vector_x += (fr_push - br_push);
151         push_vector_x *= 360;
152
153         push_vector_z = (fr_push - fl_push);
154         push_vector_z += (br_push - bl_push);
155         push_vector_z *= 360;
156
157         // Apply angle diffrance
158         this.angles_z += push_vector_z * _delta;
159         this.angles_x += push_vector_x * _delta;
160
161         // Apply stabilizer
162         this.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
163         this.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta);
164 }
165
166 void racer_fire_rocket_aim(entity player, string tagname, entity trg)
167 {
168         entity racer = player.vehicle;
169         vector v = gettaginfo(racer, gettagindex(racer, tagname));
170         racer_fire_rocket(player, v, v_forward, trg);
171 }
172
173 float racer_frame()
174 {SELFPARAM();
175         entity player, racer;
176         vector df;
177         float ftmp;
178
179         if(intermission_running)
180         {
181                 self.vehicle.velocity = '0 0 0';
182                 self.vehicle.avelocity = '0 0 0';
183                 return 1;
184         }
185
186         player  = self;
187         racer   = self.vehicle;
188         setself(racer);
189
190         vehicles_frame(racer, player);
191
192         if(pointcontents(racer.origin) != CONTENT_WATER)
193                 racer.air_finished = time + autocvar_g_vehicle_racer_water_time;
194
195         if(IS_DEAD(racer))
196         {
197                 setself(player);
198                 PHYS_INPUT_BUTTON_ATCK(player) = PHYS_INPUT_BUTTON_ATCK2(player) = false;
199                 return 1;
200         }
201
202         racer_align4point(self, PHYS_INPUT_TIMELENGTH);
203
204         PHYS_INPUT_BUTTON_ZOOM(player) = PHYS_INPUT_BUTTON_CROUCH(player) = false;
205
206         crosshair_trace(player);
207
208         racer.angles_x *= -1;
209
210         // Yaw
211         ftmp = autocvar_g_vehicle_racer_turnspeed * PHYS_INPUT_TIMELENGTH;
212         ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - racer.angles_y, racer.angles_y), ftmp);
213         racer.angles_y = anglemods(racer.angles_y + ftmp);
214
215         // Roll
216         racer.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * PHYS_INPUT_TIMELENGTH;
217
218         // Pitch
219         ftmp = autocvar_g_vehicle_racer_pitchspeed  * PHYS_INPUT_TIMELENGTH;
220         ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - racer.angles_x, racer.angles_x), ftmp);
221         racer.angles_x = bound(-autocvar_g_vehicle_racer_pitchlimit, anglemods(racer.angles_x + ftmp), autocvar_g_vehicle_racer_pitchlimit);
222
223         makevectors(racer.angles);
224         racer.angles_x *= -1;
225
226         //ftmp = racer.velocity_z;
227         df = racer.velocity * -autocvar_g_vehicle_racer_friction;
228         //racer.velocity_z = ftmp;
229
230         int cont = pointcontents(racer.origin);
231         if(player.movement)
232         {
233                 if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
234                 {
235                         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); }
236                         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); }
237                 }
238                 else
239                 {
240                         if(player.movement_x) { df += v_forward * ((player.movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); }
241                         if(player.movement_y) { df += v_right * ((player.movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); }
242                 }
243
244 #ifdef SVQC
245                 if(self.sound_nexttime < time || self.sounds != 1)
246                 {
247                         self.sounds = 1;
248                         self.sound_nexttime = time + 10.922667; //soundlength("vehicles/racer_move.wav");
249                         sound (self, CH_TRIGGER_SINGLE, SND_VEH_RACER_MOVE, VOL_VEHICLEENGINE, ATTEN_NORM);
250                 }
251 #endif
252         }
253 #ifdef SVQC
254         else
255         {
256                 if(self.sound_nexttime < time || self.sounds != 0)
257                 {
258                         self.sounds = 0;
259                         self.sound_nexttime = time + 11.888604; //soundlength("vehicles/racer_idle.wav");
260                         sound (self, CH_TRIGGER_SINGLE, SND_VEH_RACER_IDLE, VOL_VEHICLEENGINE, ATTEN_NORM);
261                 }
262         }
263 #endif
264
265         // Afterburn
266         if (PHYS_INPUT_BUTTON_JUMP(player) && racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH))
267         {
268 #ifdef SVQC
269                 if(time - racer.wait > 0.2)
270                         pointparticles(EFFECT_RACER_BOOSTER, self.origin - v_forward * 32, v_forward  * vlen(self.velocity), 1);
271 #endif
272
273                 racer.wait = time;
274
275                 if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
276                 {
277                         racer.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH;
278                         df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
279                 }
280                 else
281                 {
282                         racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH;
283                         df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn);
284                 }
285
286 #ifdef SVQC
287                 if(racer.invincible_finished < time)
288                 {
289                         traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self);
290                         if(trace_fraction != 1.0)
291                                 pointparticles(EFFECT_SMOKE_SMALL, trace_endpos, '0 0 0', 1);
292
293                         racer.invincible_finished = time + 0.1 + (random() * 0.1);
294                 }
295
296                 if(racer.strength_finished < time)
297                 {
298                         racer.strength_finished = time + 10.922667; //soundlength("vehicles/racer_boost.wav");
299                         sound (racer.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RACER_BOOST, VOL_VEHICLEENGINE, ATTEN_NORM);
300                 }
301 #endif
302         }
303         else
304         {
305                 racer.strength_finished = 0;
306                 sound (racer.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
307         }
308
309         if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
310                 racer.racer_watertime = time;
311
312         float dforce = autocvar_g_vehicle_racer_downforce;
313         if(time - racer.racer_watertime <= 3)
314                 dforce = autocvar_g_vehicle_racer_water_downforce;
315
316         df -= v_up * (vlen(racer.velocity) * dforce);
317         player.movement = racer.velocity += df * PHYS_INPUT_TIMELENGTH;
318
319 #ifdef SVQC
320         Weapon wep1 = WEP_RACER;
321         if (!forbidWeaponUse(player))
322         if (PHYS_INPUT_BUTTON_ATCK(player))
323         if (wep1.wr_checkammo1(wep1))
324         {
325                 string tagname = (racer.cnt)
326                     ? (racer.cnt = 0, "tag_fire1")
327                     : (racer.cnt = 1, "tag_fire2");
328                 vector org = gettaginfo(self, gettagindex(self, tagname));
329                 w_shotorg = org;
330                 w_shotdir = v_forward;
331                 // Fix z-aim (for chase mode)
332                 crosshair_trace(player);
333                 w_shotdir.z = normalize(trace_endpos - org).z * 0.5;
334                 .entity weaponentity = weaponentities[0];
335                 wep1.wr_think(wep1, self, weaponentity, 1);
336         }
337
338         if(autocvar_g_vehicle_racer_rocket_locktarget)
339         {
340                 vehicles_locktarget(self, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
341                                                  (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
342                                                  autocvar_g_vehicle_racer_rocket_locked_time);
343
344                 if(self.lock_target)
345                 {
346                         if(racer.lock_strength == 1)
347                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '1 0 0', 0);
348                         else if(self.lock_strength > 0.5)
349                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 1 0', 0);
350                         else if(self.lock_strength < 0.5)
351                                 UpdateAuxiliaryXhair(player, real_origin(self.lock_target), '0 0 1', 0);
352                 }
353         }
354
355         if(!forbidWeaponUse(player))
356         if(time > racer.delay)
357         if(PHYS_INPUT_BUTTON_ATCK2(player))
358         {
359                 racer.misc_bulletcounter += 1;
360                 racer.delay = time + 0.3;
361
362                 if(racer.misc_bulletcounter == 1)
363                 {
364                         racer_fire_rocket_aim(player, "tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
365                         player.vehicle_ammo2 = 50;
366                 }
367                 else if(racer.misc_bulletcounter == 2)
368                 {
369                         racer_fire_rocket_aim(player, "tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world);
370                         racer.lock_strength  = 0;
371                         racer.lock_target       = world;
372                         racer.misc_bulletcounter = 0;
373                         racer.delay = time + autocvar_g_vehicle_racer_rocket_refire;
374                         racer.lip = time;
375                         player.vehicle_ammo2 = 0;
376                 }
377         }
378         else if(racer.misc_bulletcounter == 0)
379                 player.vehicle_ammo2 = 100;
380
381         player.vehicle_reload2 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100);
382
383         if(racer.vehicle_flags  & VHF_SHIELDREGEN)
384                 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);
385
386         if(racer.vehicle_flags  & VHF_HEALTHREGEN)
387                 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);
388
389         if(racer.vehicle_flags  & VHF_ENERGYREGEN)
390                 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);
391
392
393         VEHICLE_UPDATE_PLAYER(player, health, racer);
394         VEHICLE_UPDATE_PLAYER(player, energy, racer);
395
396         if(racer.vehicle_flags & VHF_HASSHIELD)
397                 VEHICLE_UPDATE_PLAYER(player, shield, racer);
398
399         PHYS_INPUT_BUTTON_ATCK(player) = PHYS_INPUT_BUTTON_ATCK2(player) = false;
400 #endif
401
402         setorigin(player,racer.origin + '0 0 32');
403         player.velocity = racer.velocity;
404
405         setself(player);
406         return 1;
407 }
408
409 void racer_think()
410 {SELFPARAM();
411         self.nextthink = time;
412
413         float pushdeltatime = time - self.lastpushtime;
414         if (pushdeltatime > 0.15) pushdeltatime = 0;
415         self.lastpushtime = time;
416         if(!pushdeltatime) return;
417
418         tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, self);
419
420         vector df = self.velocity * -autocvar_g_vehicle_racer_friction;
421         df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2);
422
423         float forced = autocvar_g_vehicle_racer_upforcedamper;
424
425         int cont = pointcontents(self.origin - '0 0 64');
426         if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
427         {
428                 forced = autocvar_g_vehicle_racer_water_upforcedamper;
429                 self.velocity_z += 200;
430         }
431
432         self.velocity += df * pushdeltatime;
433         if(self.velocity_z > 0)
434                 self.velocity_z *= 1 - forced * pushdeltatime;
435
436         self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
437         self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
438
439         CSQCMODEL_AUTOUPDATE(self);
440 }
441
442 void racer_exit(float eject)
443 {SELFPARAM();
444         vector spot;
445
446         self.think        = racer_think;
447         self.nextthink  = time;
448         self.movetype   = MOVETYPE_BOUNCE;
449         sound (self.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
450
451         if(!self.owner)
452                 return;
453
454         makevectors(self.angles);
455         if(eject)
456         {
457                 spot = self.origin + v_forward * 100 + '0 0 64';
458                 spot = vehicles_findgoodexit(spot);
459                 setorigin(self.owner , spot);
460                 self.owner.velocity = (v_up + v_forward * 0.25) * 750;
461                 self.owner.oldvelocity = self.owner.velocity;
462         }
463         else
464         {
465                 if(vdist(self.velocity, >, 2 * autocvar_sv_maxairspeed))
466                 {
467                         self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
468                         self.owner.velocity_z += 200;
469                         spot = self.origin + v_forward * 32 + '0 0 32';
470                         spot = vehicles_findgoodexit(spot);
471                 }
472                 else
473                 {
474                         self.owner.velocity = self.velocity * 0.5;
475                         self.owner.velocity_z += 10;
476                         spot = self.origin - v_forward * 200 + '0 0 32';
477                         spot = vehicles_findgoodexit(spot);
478                 }
479                 self.owner.oldvelocity = self.owner.velocity;
480                 setorigin(self.owner , spot);
481         }
482         antilag_clear(self.owner, CS(self.owner));
483         self.owner = world;
484 }
485
486 void racer_blowup()
487 {SELFPARAM();
488         self.deadflag   = DEAD_DEAD;
489         self.vehicle_exit(VHEF_NORMAL);
490
491         RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage,
492                                         autocvar_g_vehicle_racer_blowup_edgedamage,
493                                         autocvar_g_vehicle_racer_blowup_radius, world, world,
494                                         autocvar_g_vehicle_racer_blowup_forceintensity,
495                                         DEATH_VH_WAKI_DEATH.m_id, world);
496
497         self.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
498         self.think        = vehicles_spawn;
499         self.movetype   = MOVETYPE_NONE;
500         self.effects    = EF_NODRAW;
501         self.solid = SOLID_NOT;
502
503         self.colormod  = '0 0 0';
504         self.avelocity = '0 0 0';
505         self.velocity  = '0 0 0';
506
507         setorigin(self, self.pos1);
508 }
509
510 void racer_blowup_think()
511 {SELFPARAM();
512         self.nextthink = time;
513
514         if(time >= self.delay)
515                 racer_blowup();
516
517         CSQCMODEL_AUTOUPDATE(self);
518 }
519
520 void racer_deadtouch()
521 {SELFPARAM();
522         self.avelocity_x *= 0.7;
523         self.cnt -= 1;
524         if(self.cnt <= 0)
525                 racer_blowup();
526 }
527
528 spawnfunc(vehicle_racer)
529 {
530         if(!autocvar_g_vehicle_racer) { remove(self); return; }
531         if(!vehicle_initialize(VEH_RACER, false)) { remove(self); return; }
532 }
533
534 #endif // SVQC
535
536 #ifdef CSQC
537 #if 0
538 void racer_draw()
539 {SELFPARAM();
540         float pushdeltatime = time - self.lastpushtime;
541         if (pushdeltatime > 0.15) pushdeltatime = 0;
542         self.lastpushtime = time;
543         if(!pushdeltatime) return;
544
545         tracebox(self.move_origin, self.mins, self.maxs, self.move_origin - ('0 0 1' * STAT(VEH_RACER_SPRINGLENGTH)), MOVE_NOMONSTERS, self);
546
547         vector df = self.move_velocity * -STAT(VEH_RACER_FRICTION);
548         df_z += (1 - trace_fraction) * STAT(VEH_RACER_HOVERPOWER) + sin(time * 2) * (STAT(VEH_RACER_SPRINGLENGTH) * 2);
549
550         float forced = STAT(VEH_RACER_UPFORCEDAMPER);
551
552         int cont = pointcontents(self.move_origin - '0 0 64');
553         if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
554         {
555                 forced = STAT(VEH_RACER_WATER_UPFORCEDAMPER);
556                 self.move_velocity_z += 200;
557         }
558
559         self.move_velocity += df * pushdeltatime;
560         if(self.move_velocity_z > 0)
561                 self.move_velocity_z *= 1 - forced * pushdeltatime;
562
563         self.move_angles_x *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
564         self.move_angles_z *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime);
565
566         Movetype_Physics_MatchServer(false);
567 }
568 #endif
569 #endif
570
571 METHOD(Racer, vr_impact, void(Racer thisveh, entity instance))
572 {
573 #ifdef SVQC
574     if(autocvar_g_vehicle_racer_bouncepain)
575         vehicles_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z);
576 #endif
577 }
578
579 METHOD(Racer, vr_enter, void(Racer thisveh, entity instance))
580 {
581     SELFPARAM();
582 #ifdef SVQC
583     self.movetype = MOVETYPE_BOUNCE;
584     self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_racer_health)  * 100;
585     self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_racer_shield)  * 100;
586
587     if(self.owner.flagcarried)
588        setorigin(self.owner.flagcarried, '-190 0 96');
589 #elif defined(CSQC)
590
591     self.move_movetype = MOVETYPE_BOUNCE;
592 #endif
593 }
594
595 METHOD(Racer, vr_spawn, void(Racer thisveh, entity instance))
596 {
597     SELFPARAM();
598 #ifdef SVQC
599     if(self.scale != 0.5)
600     {
601         if(autocvar_g_vehicle_racer_hovertype != 0)
602             racer_force_from_tag = vehicles_force_fromtag_maglev;
603         else
604             racer_force_from_tag = vehicles_force_fromtag_hover;
605
606         // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
607         self.scale = 0.5;
608         setattachment(self.vehicle_hudmodel, self, "");
609         setattachment(self.vehicle_viewport, self, "tag_viewport");
610
611         self.mass                          = 900;
612     }
613
614     self.think            = racer_think;
615     self.nextthink        = time;
616     self.vehicle_health = autocvar_g_vehicle_racer_health;
617     self.vehicle_shield = autocvar_g_vehicle_racer_shield;
618
619     self.movetype         = MOVETYPE_TOSS;
620     self.solid            = SOLID_SLIDEBOX;
621     self.delay            = time;
622     self.scale            = 0.5;
623
624     self.PlayerPhysplug = racer_frame;
625
626     self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
627     self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
628     self.damageforcescale = 0.5;
629     self.vehicle_health = autocvar_g_vehicle_racer_health;
630     self.vehicle_shield = autocvar_g_vehicle_racer_shield;
631 #endif
632 }
633
634 METHOD(Racer, vr_death, void(Racer thisveh, entity instance))
635 {
636 #ifdef SVQC
637     instance.SendEntity         = func_null; // stop networking this racer (for now)
638     instance.health                     = 0;
639     instance.event_damage       = func_null;
640     instance.solid                      = SOLID_CORPSE;
641     instance.takedamage         = DAMAGE_NO;
642     instance.deadflag           = DEAD_DYING;
643     instance.movetype           = MOVETYPE_BOUNCE;
644     instance.wait                       = time;
645     instance.delay                      = 2 + time + random() * 3;
646     instance.cnt                        = 1 + random() * 2;
647     instance.touch                      = racer_deadtouch;
648
649     Send_Effect(EFFECT_EXPLOSION_MEDIUM, instance.origin, '0 0 0', 1);
650
651     if(random() < 0.5)
652         instance.avelocity_z = 32;
653     else
654         instance.avelocity_z = -32;
655
656     instance.avelocity_x = -vlen(instance.velocity) * 0.2;
657     instance.velocity += '0 0 700';
658     instance.colormod = '-0.5 -0.5 -0.5';
659
660     instance.think = racer_blowup_think;
661     instance.nextthink = time;
662 #endif
663 }
664
665 #ifdef CSQC
666 METHOD(Racer, vr_hud, void(Racer thisveh))
667 {
668     Vehicles_drawHUD(VEH_RACER.m_icon, "vehicle_racer_weapon1", "vehicle_racer_weapon2",
669                      "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
670                      "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color);
671 }
672 METHOD(Racer, vr_crosshair, void(Racer thisveh))
673 {
674     Vehicles_drawCrosshair(vCROSS_GUIDE);
675 }
676 #endif
677 METHOD(Racer, vr_setup, void(Racer thisveh, entity instance))
678 {
679     SELFPARAM();
680 #ifdef SVQC
681     self.vehicle_exit = racer_exit;
682 #endif
683
684 #ifdef SVQC
685     // we have no need to network energy
686     if(autocvar_g_vehicle_racer_energy)
687     if(autocvar_g_vehicle_racer_energy_regen)
688         self.vehicle_flags |= VHF_ENERGYREGEN;
689
690     if(autocvar_g_vehicle_racer_shield)
691         self.vehicle_flags |= VHF_HASSHIELD;
692
693     if(autocvar_g_vehicle_racer_shield_regen)
694         self.vehicle_flags |= VHF_SHIELDREGEN;
695
696     if(autocvar_g_vehicle_racer_health_regen)
697         self.vehicle_flags |= VHF_HEALTHREGEN;
698
699     self.respawntime = autocvar_g_vehicle_racer_respawntime;
700     self.vehicle_health = autocvar_g_vehicle_racer_health;
701     self.vehicle_shield = autocvar_g_vehicle_racer_shield;
702     self.max_health = self.vehicle_health;
703 #endif
704
705 #ifdef CSQC
706     AuxiliaryXhair[0].axh_image = vCROSS_LOCK; // Rocket
707 #endif
708 }
709
710 #endif