]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/vehicles/racer.qc
fix cursor trace for chase cam
[divverent/nexuiz.git] / data / qcsrc / server / vehicles / racer.qc
1 #define RACER_MIN '-80 -80 -40'
2 #define RACER_MAX '80 80 40'
3 #define RACER_TICRATE 0.05
4
5 void racer_exit(float eject);
6 void racer_enter();
7 //void racer_think();
8
9 float racer_power_air;
10 float racer_power_solid;
11 float racer_power_min;
12 float racer_anglestabilizer;
13 float racer_springlength;
14
15 float racer_healthmax;
16 float racer_shieldmax;
17 float racer_energymax;
18
19 float racer_pitchspeed;
20 float racer_turnspeed;
21 float racer_turnroll;
22 float racer_speed_forward;
23 float racer_speed_strafe;
24
25 float  jetfromtag_power;
26 float  jetfromtag_normpower;
27 vector jetfromtag_origin;
28 float  jetfromtag_groundcontact;
29
30 void racer_loadsettings()
31 {
32     racer_power_min         = cvar("g_vehicle_racer_power_min")   * 0.25;
33     racer_power_air         = cvar("g_vehicle_racer_power_air")   * 0.25;
34     racer_power_solid       = cvar("g_vehicle_racer_power_solid") * 0.25;
35
36     racer_springlength      = cvar("g_vehicle_racer_springlength");
37     racer_anglestabilizer   = cvar("g_vehicle_racer_anglestabilizer");
38     racer_pitchspeed        = cvar("g_vehicle_racer_pitchspeed");
39     racer_turnspeed         = cvar("g_vehicle_racer_turnspeed");
40     racer_turnroll          = cvar("g_vehicle_racer_turnroll");
41     racer_speed_forward     = cvar("g_vehicle_racer_speed_forward");
42     racer_speed_strafe      = cvar("g_vehicle_racer_speed_strafe");
43
44     racer_healthmax = cvar("g_vehicle_racer_health");
45     racer_shieldmax = cvar("g_vehicle_racer_shield");
46     racer_energymax = cvar("g_vehicle_racer_energy");
47
48 }
49
50 vector racer_jetfromtag(string tagname)
51 {
52     vector force_dir;
53     float  air_frac, solid_frac, air_pwr, solid_pwr;
54
55     jetfromtag_origin = gettaginfo(self,gettagindex(self,tagname));
56     v_forward  = normalize(v_forward) * -1;
57
58     force_dir = jetfromtag_origin - (v_forward  * racer_springlength);
59     traceline(jetfromtag_origin, force_dir, MOVE_NORMAL, self);
60     //te_lightning1(world,jetfromtag_origin, force_dir);
61
62     //trace_fraction *= trace_fraction;
63
64     solid_frac = 1 - trace_fraction;
65     air_frac = trace_fraction;
66
67     if (trace_fraction != 1.0)
68         jetfromtag_groundcontact = 1;
69
70     solid_pwr = solid_frac * racer_power_solid;
71     air_pwr   = air_frac * racer_power_air;
72
73     jetfromtag_power     = solid_pwr + air_pwr;
74     jetfromtag_normpower = jetfromtag_power / (racer_power_air + racer_power_solid);
75
76     //te_lightning1(world,jetfromtag_origin,jetfromtag_origin + normalize(self.origin - force_dir) * max(jetfromtag_power, racer_power_min));
77     //return normalize(self.origin - force_dir) * max(jetfromtag_power, racer_power_min);
78     return v_forward  * max(jetfromtag_power, racer_power_min);
79 }
80
81 void racer_align4point()
82 {
83     vector push_vector;
84     float fl_push, fr_push, bl_push, br_push;
85
86     jetfromtag_groundcontact = 0;
87
88     push_vector = racer_jetfromtag("tag_engine_fr");
89     fr_push = jetfromtag_normpower;
90     traceline(jetfromtag_origin, jetfromtag_origin + self.velocity * frametime, MOVE_NORMAL,self);
91     if(trace_fraction != 1)
92         self.velocity += normalize(self.origin - trace_endpos) * (vlen(self.velocity) * 1.25);
93
94     push_vector += racer_jetfromtag("tag_engine_fl");
95     fl_push = jetfromtag_normpower;
96     traceline(jetfromtag_origin, jetfromtag_origin + self.velocity * frametime, MOVE_NORMAL,self);
97     if(trace_fraction != 1)
98         self.velocity += normalize(self.origin - trace_endpos) * (vlen(self.velocity) * 1.25);
99
100     push_vector += racer_jetfromtag("tag_engine_br");
101     br_push = jetfromtag_normpower;
102     traceline(jetfromtag_origin, jetfromtag_origin + self.velocity * frametime, MOVE_NORMAL,self);
103     if(trace_fraction != 1)
104         self.velocity += normalize(self.origin - trace_endpos) * (vlen(self.velocity) * 1.25);
105
106     push_vector += racer_jetfromtag("tag_engine_bl");
107     bl_push = jetfromtag_normpower;
108     traceline(jetfromtag_origin, jetfromtag_origin + self.velocity * frametime, MOVE_NORMAL,self);
109     if(trace_fraction != 1)
110         self.velocity += normalize(self.origin - trace_endpos) * (vlen(self.velocity) * 1.25);
111
112     self.velocity =  self.velocity + (push_vector * frametime);
113     self.velocity_z -= sv_gravity * frametime;
114
115     push_vector_x = (fl_push - bl_push);
116     push_vector_x += (fr_push - br_push);
117     push_vector_x *= 360;
118
119     push_vector_z = (fr_push - fl_push);
120     push_vector_z += (br_push - bl_push);
121     push_vector_z *= 360;
122
123     //if (push_vector_z != 0)
124         if(self.angles_z > 0)
125             self.angles_z = max(0, self.angles_z - (racer_anglestabilizer * frametime));
126         else
127             self.angles_z = min(0, self.angles_z + (racer_anglestabilizer * frametime));
128     //else
129         self.angles_z += push_vector_z * frametime;
130
131     //if (push_vector_x != 0)
132         if(self.angles_x > 0)
133             self.angles_x = max(0, self.angles_x - (racer_anglestabilizer * frametime));
134         else
135             self.angles_x = min(0, self.angles_x + (racer_anglestabilizer * frametime));
136     //else
137         self.angles_x += push_vector_x * frametime;
138 }
139
140 void racer_bolt_explode()
141 {
142     vector org2;
143
144     org2 = findbetterlocation (self.origin, 8);
145     pointparticles(particleeffectnum("laser_impact"), org2, trace_plane_normal * 1000, 1);
146     RadiusDamage (self, self.realowner, cvar("g_vehicle_racer_laser_damage"), 0, cvar("g_vehicle_racer_laser_radius"), world, 150, DEATH_WAKIGUN, world);
147     sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
148
149     remove (self);
150 }
151
152 void racer_rocket_explode()
153 {
154     vector org2;
155
156     //if not(self.owner)
157     //    self.owner = self.realowner;
158
159     sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
160     org2 = findbetterlocation (self.origin, 16);
161     pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
162     RadiusDamage (self, self.realowner ,cvar("g_vehicle_racer_rocket_damage"), 0, cvar("g_vehicle_racer_rocket_radius"), world, 150, DEATH_WAKIROCKET, world);
163
164     remove (self);
165 }
166
167 void racer_rocket_touch()
168 {
169     if(other.owner == self.owner)
170         return;
171
172     PROJECTILE_TOUCH;
173     /*
174     if(pointcontents(self.origin) == CONTENT_SKY)
175     {
176         self.think = SUB_Remove;
177         self.nextthink = time;
178         return;
179     }
180     */
181
182     racer_rocket_explode();
183 }
184
185 void racer_fire_cannon(string tagname)
186 {
187     entity bolt;
188
189     bolt = spawn();
190     bolt.solid           = SOLID_TRIGGER;
191     bolt.movetype        = MOVETYPE_FLYMISSILE;
192     bolt.flags           = FL_PROJECTILE | FL_NOTARGET;
193     bolt.owner           = self;
194     bolt.realowner       = self.owner;
195     bolt.touch           = racer_bolt_explode;
196     bolt.think           = racer_bolt_explode;
197     bolt.nextthink       = time + 9;
198     bolt.bot_dodge       = TRUE;
199     bolt.bot_dodgerating = cvar("g_vehicle_racer_laser_damage");
200     setorigin(bolt, gettaginfo(self,gettagindex(self,tagname)));
201
202     vector v;
203         // FIXME this uses prydon cursor
204     v = normalize(self.owner.cursor_trace_endpos - bolt.origin);
205     v_forward_z = v_z * 0.5;
206     //v_forward_z *= 0.5;
207
208     bolt.velocity = v_forward * cvar("g_vehicle_racer_laser_speed");
209
210     CSQCProjectile(bolt, TRUE, PROJECTILE_LASER, TRUE);
211 }
212
213 void racer_rocket_groundhugger()
214 {
215     vector newdir,olddir;
216     float oldspeed, newspeed;
217
218     self.nextthink  = time;
219
220     if ((self.owner.deadflag != DEAD_NO) || (self.cnt < time))
221     {
222         racer_rocket_explode();
223         return;
224     }
225
226     if not (self.realowner.vehicle)
227     {
228         UpdateCSQCProjectile(self);
229         return;
230     }
231
232     olddir   = normalize(self.velocity);
233     oldspeed = vlen(self.velocity);
234
235     tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 512, MOVE_WORLDONLY,self);
236     if(trace_fraction >= 0.35)
237     {
238         traceline(trace_endpos, trace_endpos - '0 0 512', MOVE_NORMAL, self);
239         if(trace_fraction != 1.0)
240             newdir = trace_endpos + '0 0 32';
241
242         newdir = normalize(newdir - self.origin);
243     }
244
245     newspeed = oldspeed + self.lip;
246     self.velocity = normalize(olddir + newdir * self.wait) * newspeed;
247
248     UpdateCSQCProjectile(self);
249 }
250
251 void racer_rocket_think()
252 {
253     vector newdir,olddir, eorg, edir;
254     float oldspeed, newspeed, dist;
255
256     self.nextthink  = time;
257
258     if ((self.owner.deadflag != DEAD_NO) || (self.cnt < time))
259     {
260         racer_rocket_explode();
261         return;
262     }
263
264     if not (self.realowner.vehicle)
265     {
266         UpdateCSQCProjectile(self);
267         return;
268     }
269
270     olddir = normalize(self.velocity);
271     oldspeed = vlen(self.velocity);
272     fixedmakevectors(olddir);
273     eorg = 0.5 * (self.enemy.absmin + self.enemy.absmax);
274     edir = normalize(eorg - self.origin);
275     dist = vlen(self.origin - self.enemy.origin);
276
277     if(dist > 512)
278     {
279         vector ddir;
280         float p;
281         p = dist / self.delay;
282         p = max(0.05, p * 0.25);
283         ddir = steerlib_traceavoid(p, oldspeed * 0.25);
284         edir += ddir * 0.95;
285     }
286
287     newdir = normalize(olddir + edir * self.wait);
288     newspeed = oldspeed + self.lip;
289     self.velocity = normalize(olddir + newdir * self.wait) * newspeed;
290
291     UpdateCSQCProjectile(self);
292 }
293
294 void racer_fire_rocket(string tagname)
295 {
296     entity rocket;
297
298     rocket = spawn();
299     setsize (rocket, '-1 -1 -1', '1 1 1');
300     rocket.lip             = cvar("g_vehicle_racer_rocket_accel") * sys_frametime;
301     rocket.wait            = cvar("g_vehicle_racer_rocket_turnrate");
302     rocket.solid           = SOLID_BBOX;
303     rocket.movetype        = MOVETYPE_FLYMISSILE;
304     rocket.flags           = FL_PROJECTILE;
305     rocket.owner           = self;
306     rocket.realowner       = self.owner;
307     rocket.touch           = racer_rocket_touch;
308     rocket.bot_dodge       = TRUE;
309     rocket.bot_dodgerating = cvar("g_vehicle_racer_rocket_damage");
310     setorigin(rocket, gettaginfo(self,gettagindex(self,tagname)));
311
312     /*
313     if( (self.tur_head.gun1) && (self.tur_head.cnt > time))
314         rocket.enemy = self.tur_head.gun1;
315
316     if(rocket.enemy)
317     {
318         rocket.delay           = vlen(rocket.enemy.origin  - rocket.origin);
319         rocket.cnt             = time + 9;
320         rocket.velocity = v_forward * cvar("g_vehicle_racer_rocket_speed");
321         rocket.think = racer_rocket_think;
322         rocket.nextthink = time;
323         CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound
324     }
325     else
326     {
327     */
328         rocket.cnt             = time + 9;
329         rocket.velocity = v_forward * cvar("g_vehicle_racer_rocket_speed");
330         rocket.think = racer_rocket_groundhugger;
331         rocket.nextthink = time;
332         CSQCProjectile(rocket, TRUE, PROJECTILE_ROCKET, TRUE);
333     //}
334 }
335
336 float racer_customizeentityforclient()
337 {
338
339     if(self.deadflag == DEAD_DEAD)
340         return FALSE;
341
342     /*
343     if(other == self.owner)
344         self.alpha = -1;
345     else
346         self.alpha = 1;
347     */
348
349     return TRUE;
350 }
351
352 float racer_pplug()
353 {
354     entity player, racer;
355     float ftmp, ftmp2;
356     vector df;
357
358
359     if(cvar("g_vehicle_racer_reload"))
360     {
361         racer_loadsettings();
362         cvar_set("g_vehicle_racer_reload","0");
363     }
364
365     player          = self;
366     racer           = self.vehicle;
367
368     player.BUTTON_ZOOM = 0;
369     player.BUTTON_CROUCH = 0;
370
371     self = racer;
372
373     if(player.BUTTON_USE)
374     {
375         self = racer;
376         racer_exit(0);
377         self = player;
378         return 0;
379     }
380
381     if(racer.deadflag != DEAD_NO)
382     {
383         self = player;
384         player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
385         return 1;
386     }
387
388
389     racer_align4point();
390
391     racer.angles_x *= -1;
392     makevectors(racer.angles);
393
394     // Rotate Body
395     ftmp = racer_turnspeed * sys_frametime;
396     ftmp2 = ftmp * -1;
397
398     ftmp = bound(ftmp2,shortangle_f(player.v_angle_y - racer.angles_y,racer.angles_y),ftmp);
399     ftmp2 = safeangle(racer.angles_y + ftmp);
400
401     // Roll
402     ftmp = bound(-45,shortangle_f(player.v_angle_z + ((racer.angles_y - ftmp2) * racer_turnroll), racer.angles_z),45);
403     ftmp = safeangle(racer.angles_z + ftmp);
404     racer.angles_z = bound(-85,ftmp,85);
405
406     // Turn
407     racer.angles_y = ftmp2;
408
409     // Pitch Body
410     ftmp = racer_pitchspeed * sys_frametime;
411     ftmp2 = ftmp * -1;
412
413     ftmp = bound(ftmp2,shortangle_f(player.v_angle_x - racer.angles_x,racer.angles_x),ftmp);
414     racer.angles_x = safeangle(racer.angles_x + ftmp);
415     racer.angles_x *= -1;
416
417     df = racer.velocity * -1;
418
419     if(player.movement_x != 0)
420     {
421         if(player.movement_x > 0)
422             df += v_forward  * racer_speed_forward;
423         else if(player.movement_x < 0)
424             df -= v_forward  * racer_speed_forward;
425     }
426
427     if(player.movement_y != 0)
428     {
429         if(player.movement_y < 0)
430             df -= v_right * racer_speed_strafe;
431         else if(player.movement_y > 0)
432             df += v_right * racer_speed_strafe;
433     }
434
435     // limit _z to avoid flying on normal thrust
436     if(df_z > 0)
437         df_z = min(df_z,700);
438
439
440     // Afterburn
441     //if (jetfromtag_groundcontact)
442     if (player.BUTTON_JUMP)
443     if(racer.vehicle_energy >= (cvar("g_vehicle_racer_speed_afterburn_cost") * frametime))
444     {
445         racer.wait = time + cvar("g_vehicle_racer_energy_usepause");
446         racer.vehicle_energy -= cvar("g_vehicle_racer_speed_afterburn_cost") * frametime;
447         df += (v_forward * cvar("g_vehicle_racer_speed_afterburn"));
448     }
449
450     racer.velocity  += df * frametime;
451
452     df = (vlen(racer.velocity) * cvar("g_vehicle_racer_downforce") * v_up) * frametime;
453     racer.velocity  = racer.velocity - df;
454     player.movement = racer.velocity;
455
456     /*
457     // for homing rockets
458         // FIXME this uses prydon cursor
459     if(self.owner.cursor_trace_ent)
460     {
461         self.tur_head.gun1 = self.owner.cursor_trace_ent;
462         self.tur_head.cnt = time + 1;
463     }
464     */
465
466     if(player.BUTTON_ATCK)
467     if(time > racer.attack_finished_single)
468     if(racer.vehicle_energy >= cvar("g_vehicle_racer_laser_cost"))
469     {
470         racer.vehicle_energy -= cvar("g_vehicle_racer_laser_cost");
471         racer.wait = time + cvar("g_vehicle_racer_energy_usepause");
472
473         if(racer.cnt)
474         {
475             racer_fire_cannon("tag_fire1");
476             racer.cnt = 0;
477         }
478         else
479         {
480             racer_fire_cannon("tag_fire2");
481             racer.cnt = 1;
482         }
483         racer.attack_finished_single = time + cvar("g_vehicle_racer_laser_refire");
484     }
485
486     if(player.BUTTON_ATCK2)
487     if(time > racer.delay)
488     {
489         racer_fire_rocket("tag_rocket_r");
490         racer_fire_rocket("tag_rocket_l");
491         racer.delay = time + cvar("g_vehicle_racer_rocket_refire");
492         racer.lip = time;
493
494     }
495
496     player.vehicle_reload1 = (time - racer.lip) / (racer.delay-racer.lip);
497     //player.vehicle_energy = racer.vehicle_energy;
498
499     vehicle_stdproc_shiledregen(racer_shieldmax, frametime);
500     vehicle_stdproc_healthregen(racer_healthmax, frametime);
501
502     if (racer.wait < time)
503         vehicle_stdproc_energyregen(racer_energymax, frametime);
504     else
505         player.vehicle_energy = (racer.vehicle_energy / racer_energymax);
506
507     self = player;
508
509     player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
510     setorigin(player,racer.origin + '0 0 32');
511     player.velocity = racer.velocity;
512
513     return 1;
514 }
515
516 void racer_exitthink()
517 {
518     float a, b, c;
519
520     self.nextthink = time;
521
522     a = racer_anglestabilizer;
523     b = racer_springlength;
524     c = racer_power_solid;
525
526     racer_anglestabilizer = 36;
527     racer_springlength = 96;
528     racer_power_solid = 300;
529
530     racer_align4point();
531
532     if(self.velocity_z < 0)
533         self.velocity_z *= 0.95;
534
535     racer_anglestabilizer = a;
536     racer_springlength = b;
537     racer_power_solid = c;
538
539     self.velocity_x *= 0.95;
540     self.velocity_y *= 0.95;
541 }
542
543 void racer_spawnthink()
544 {
545     float a, b, c;
546     vector dorg;
547
548     self.nextthink = time;
549
550     a = racer_anglestabilizer;
551     b = racer_springlength;
552     c = racer_power_solid;
553
554     racer_anglestabilizer = 36;
555     racer_springlength = 96;
556     racer_power_solid = 300;
557
558     // self.velocity_z += sin(time * 128) * 4;
559
560     racer_align4point();
561
562     if(self.velocity_z < 0)
563         self.velocity_z *= 0.98;
564
565     racer_anglestabilizer = a;
566     racer_springlength = b;
567     racer_power_solid = c;
568
569     self.velocity_x *= 0.95;
570     self.velocity_y *= 0.95;
571
572     dorg = self.pos1 - self.origin ;
573
574     self.velocity_x = bound(-32, self.velocity_x + dorg_x, 32);
575     self.velocity_y = bound(-32, self.velocity_y + dorg_y, 32);
576 }
577
578 void racer_enter()
579 {
580     self.owner = other;
581
582
583     self.nextthink = 0;
584     self.flags               = 0;
585     self.vehicle_hudmodel.viewmodelforclient = self.owner;
586     self.colormap             = self.owner.colormap;
587
588     self.owner.PlayerPhysplug = racer_pplug;
589     self.owner.takedamage     = DAMAGE_NO;
590     self.owner.event_damage   = SUB_Null;
591     self.owner.vehicle        = self;
592     self.owner.angles = self.angles;
593     self.owner.solid          = SOLID_NOT;
594     self.owner.movetype       = MOVETYPE_NOCLIP;
595     self.owner.alpha          = -1;
596     self.owner.hud            = HUD_WAKIZASHI;
597     self.owner.effects        |= EF_NODRAW;
598     self.owner.view_ofs = '0 0 0';
599
600     self.owner.vehicle_health = (self.vehicle_health / cvar("g_vehicle_racer_health"));
601     self.owner.vehicle_shield = (self.vehicle_shield / cvar("g_vehicle_racer_shield"));
602
603     setorigin(other,self.origin + '0 0 32');
604     other.velocity = self.velocity;
605
606     other.flags &~= FL_ONGROUND;
607     msg_entity = other;
608     WriteByte (MSG_ONE, SVC_SETVIEWPORT);
609     WriteEntity( MSG_ONE, self.vehicle_viewport);
610
611     WriteByte (MSG_ONE, SVC_SETVIEWANGLES); // 10 = SVC_SETVIEWANGLES
612     WriteAngle(MSG_ONE,  self.angles_x * -1);    // tilt
613     WriteAngle(MSG_ONE,  self.angles_y);    // yaw
614     WriteAngle(MSG_ONE,  0);                // roll
615 }
616
617 void racer_spawn();
618 void racer_return()
619 {
620     pointparticles(particleeffectnum("teleport"), self.enemy.origin + '0 0 64', '0 0 0', 1);
621     self.enemy.think = racer_spawn;
622     self.enemy.nextthink = time;
623     remove(self);
624 }
625
626 void racer_exit(float eject)
627 {
628         self.colormap   = 1024;
629         self.flags      = FL_NOTARGET;
630
631     if not (self.owner)
632         return;
633
634     msg_entity = self.owner;
635     WriteByte (MSG_ONE, SVC_SETVIEWPORT);
636     WriteEntity( MSG_ONE, self.owner);
637
638     WriteByte (MSG_ONE, SVC_SETVIEWANGLES);    // 10 = SVC_SETVIEWANGLES
639     WriteAngle(MSG_ONE,  0);                   // tilt
640     WriteAngle(MSG_ONE,  self.angles_y); // yaw
641     WriteAngle(MSG_ONE,  0);                   // roll
642
643     if (self.deadflag == DEAD_NO)
644     {
645         self.think = racer_exitthink;
646         self.nextthink = time;
647     }
648
649     self.owner.takedamage     = DAMAGE_AIM;
650     self.owner.solid          = SOLID_SLIDEBOX;
651     self.owner.movetype       = MOVETYPE_WALK;
652
653     setsize(self.owner,PL_MIN,PL_MAX);
654
655     self.owner.effects        &~= EF_NODRAW;
656     self.owner.alpha          = 1;
657     self.owner.PlayerPhysplug = SUB_Null;
658     self.owner.vehicle        = world;
659         self.owner.view_ofs       = PL_VIEW_OFS;
660         self.owner.event_damage   = PlayerDamage;
661         self.owner.hud            = HUD_NORMAL;
662
663     self.vehicle_hudmodel.viewmodelforclient = self;
664
665         if(eject)
666         {
667             makevectors(self.angles);
668             setorigin(self.owner,self.origin + v_forward * 100);
669             self.owner.velocity = (v_up + v_forward * 0.25) * 750;
670         }
671         else
672         {
673             self.owner.velocity = (v_forward) * -150;
674         setorigin(self.owner,self.origin - v_forward * 128);
675         }
676
677     self.owner = world;
678
679     if (self.deadflag != DEAD_NO)
680     {
681         entity ret;
682         ret = spawn();
683         ret.enemy = self;
684         ret.think = racer_return;
685         ret.nextthink = time + cvar("g_vehicle_racer_respawntime");
686     }
687 }
688
689 void racer_touch()
690 {
691     if(self.owner)
692     {
693         if(vlen(self.velocity) == 0)
694             return;
695
696         if(other.classname != "player")
697             return;
698
699         vector a;
700         a = normalize(other.origin - self.origin);
701         a = a - normalize(self.velocity);
702
703         return;
704     }
705
706     if(other.classname != "player")
707         return;
708
709     if(other.deadflag != DEAD_NO)
710         return;
711
712     if(other.vehicle != world)
713         return;
714
715     racer_enter();
716 }
717
718 /*
719 float racer_customizeentityforclient()
720 {
721     if(self.deadflag == DEAD_DEAD)
722         return FALSE;
723
724     return TRUE;
725 }
726 */
727
728 void racer_spawn()
729 {
730     self.think = racer_spawnthink;
731     self.nextthink = time;
732
733     self.flags      = FL_NOTARGET;
734     self.effects   = 0;
735
736     self.vehicle_health = racer_healthmax;
737     self.vehicle_shield = racer_shieldmax;
738
739     self.event_damage = vehicle_stdproc_damage;
740     self.touch      = racer_touch;
741
742     self.iscreature = TRUE;
743     self.scale      = 0.5;
744     self.movetype   = MOVETYPE_FLY;
745     self.solid      = SOLID_SLIDEBOX;
746     self.takedamage = DAMAGE_AIM;
747
748     self.alpha = 1;
749         self.colormap = 1024;
750         self.deadflag    = DEAD_NO;
751     self.bot_attack = TRUE;
752
753     self.vehicle_energy = 1;
754     self.vehicle_hudmodel.viewmodelforclient = self;
755
756     setorigin(self, self.pos1);
757     self.angles = self.pos2;
758
759     setsize(self,RACER_MIN * 0.5,RACER_MAX * 0.5);
760     pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
761     self.delay = time;
762 }
763
764
765 void racer_blowup()
766 {
767
768     sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
769     pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
770
771     self.deadflag    = DEAD_DEAD;
772     self.vehicle_exit(VHEF_NORMAL);
773     RadiusDamage (self, self, 250, 15, 250, world, 250, DEATH_WAKIBLOWUP, world);
774
775     self.alpha = -1;
776     self.nextthink  = time + cvar("g_vehicle_racer_respawntime");
777     self.think      = racer_spawn;
778     self.movetype   = MOVETYPE_NONE;
779     self.effects    = EF_NODRAW;
780
781     self.avelocity_z  = 0;
782     self.colormod = '0 0 0';
783
784     setorigin(self,self.pos1);
785 }
786
787 void racer_dietouch()
788 {
789     if(self.wait > time)
790         return;
791
792     self.wait = time + 0.75;
793
794     pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
795 }
796
797 void racer_die()
798 {
799     self.health       = 0;
800     self.event_damage = SUB_Null;
801     self.iscreature   = FALSE;
802     self.solid        = SOLID_NOT;
803     self.takedamage   = DAMAGE_NO;
804     self.touch        = racer_dietouch;
805     self.deadflag     = DEAD_DYING;
806     self.movetype     = MOVETYPE_BOUNCE;
807     self.wait = time;
808
809     pointparticles(particleeffectnum("rocket_explode"), findbetterlocation (self.origin, 16), '0 0 0', 1);
810
811     self.velocity     += '0 0 128';
812
813     if(random() < 0.5)
814         self.avelocity_z  = 16;
815     else
816         self.avelocity_z  = -16;
817
818     self.colormod = '-0.5 -0.5 -0.5';
819
820         self.think     = racer_blowup;
821         self.nextthink = time + 3;
822 }
823
824 void racer_dinit()
825 {
826
827     tracebox(self.origin + '0 0 100', RACER_MIN * 0.5, RACER_MAX * 0.5, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
828     setorigin(self,trace_endpos);
829
830     if (self.netname == "")
831         self.netname     = "Race PoD";
832
833     self.cvar_basename      = "g_vehicle_racer";
834
835     self.vehicle_viewport   = spawn();
836     self.vehicle_hudmodel   = spawn();
837     //self.ccamera            = spawn();
838
839     setorigin(self, self.origin);
840
841     setmodel(self,"models/vehicles/wakizashi.dpm");
842     setmodel(self.vehicle_hudmodel, "models/vehicles/wakizashi_cockpit.dpm");
843     setmodel (self.vehicle_viewport, "null");
844
845     setattachment(self.vehicle_hudmodel, self, "");
846     setattachment(self.vehicle_viewport, self, "tag_viewport");
847
848     self.vehicle_hudmodel.viewmodelforclient = self;
849     self.customizeentityforclient            = racer_customizeentityforclient;
850
851     self.tur_head     = spawn();
852
853     self.pos1         = self.origin;
854     self.pos2         = self.angles;
855     self.angles       = '0 0 0';
856     self.angles       = self.pos1;
857
858     self.vehicle_die  = racer_die;
859     self.vehicle_exit = racer_exit;
860
861     addstat(STAT_HUD, AS_INT,  hud);
862         addstat(STAT_VEHICLESTAT_HEALTH,  AS_FLOAT, vehicle_health);
863         addstat(STAT_VEHICLESTAT_SHIELD,  AS_FLOAT, vehicle_shield);
864         addstat(STAT_VEHICLESTAT_ENERGY,  AS_FLOAT, vehicle_energy);
865
866         addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT,   vehicle_ammo1);
867         addstat(STAT_VEHICLESTAT_RELOAD1, AS_FLOAT, vehicle_reload1);
868
869         addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT,   vehicle_ammo2);
870         addstat(STAT_VEHICLESTAT_RELOAD2, AS_FLOAT, vehicle_reload2);
871
872     racer_spawn();
873 }
874
875 void spawnfunc_vehicle_racer()
876 {
877     g_turrets_common_precash();
878     racer_loadsettings();
879
880     self.vehicle_flags      = VHF_HASSHIELD | VHF_SHIELDREGEN;
881
882     /*
883     traceline(self.origin,self.origin - '0 0 2048',MOVE_WORLDONLY,self);
884     if(trace_startsolid)
885     {
886         dprint("WARNING: vehicle_racer placed in solid\n");
887         traceline(self.origin + '0 0 512' ,self.origin - '0 0 2048',MOVE_WORLDONLY,self);
888         if(trace_startsolid || trace_fraction == 1.0)
889         {
890             dprint("ERROR: vehicle_racer placed in more then 512 units into solid\n");
891             remove(self);
892             return;
893         }
894     }
895     */
896
897     if(trace_fraction != 1.0)
898         setorigin(self,trace_endpos + '0 0 128');
899     else
900         dprint("WARNING: vehicle_racer placed more then 2048 units above ground.\n");
901
902     precache_model ("models/vehicles/wakizashi.dpm");
903     precache_model ("models/vehicles/wakizashi_cockpit.dpm");
904     precache_model ("maps/bspmodel.bsp");
905
906
907     self.think = racer_dinit;
908     self.nextthink = time + 1;
909 }