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