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