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