]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/vehicles/vehicles.qc
Make most server includes order insensitive
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / vehicles / vehicles.qc
1 #include "vehicles.qh"
2
3 #include "../cl_player.qh"
4 #include "../waypointsprites.qh"
5
6 #include "../bot/waypoints.qh"
7
8 .float() PlayerPhysplug;
9
10 float autocvar_g_vehicles_crush_dmg;
11 float autocvar_g_vehicles_crush_force;
12 float autocvar_g_vehicles_delayspawn;
13 float autocvar_g_vehicles_delayspawn_jitter;
14
15 float autocvar_g_vehicles_vortex_damagerate = 0.5;
16 float autocvar_g_vehicles_machinegun_damagerate = 0.5;
17 float autocvar_g_vehicles_rifle_damagerate = 0.75;
18 float autocvar_g_vehicles_vaporizer_damagerate = 0.001;
19 float autocvar_g_vehicles_tag_damagerate = 5;
20
21 float autocvar_g_vehicles;
22
23 void vehicles_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
24 void vehicles_return();
25 void vehicles_enter();
26 void vehicles_touch();
27 void vehicles_reset_colors();
28 void vehicles_clearreturn();
29 void vehicles_setreturn();
30
31
32 /** AuxiliaryXhair*
33     Send additional points of interest to be drawn, to vehicle owner
34 **/
35 const float MAX_AXH = 4;
36 .entity AuxiliaryXhair[MAX_AXH];
37
38 float SendAuxiliaryXhair(entity to, int sf)
39 {
40
41         WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
42
43         WriteByte(MSG_ENTITY, self.cnt);
44
45         WriteCoord(MSG_ENTITY, self.origin.x);
46         WriteCoord(MSG_ENTITY, self.origin.y);
47         WriteCoord(MSG_ENTITY, self.origin.z);
48
49     WriteByte(MSG_ENTITY, rint(self.colormod.x * 255));
50     WriteByte(MSG_ENTITY, rint(self.colormod.y * 255));
51     WriteByte(MSG_ENTITY, rint(self.colormod.z * 255));
52
53     return true;
54 }
55
56 void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id)
57 {
58     if (!IS_REAL_CLIENT(own))
59         return;
60
61     entity axh;
62
63     axh_id = bound(0, axh_id, MAX_AXH);
64     axh = own.(AuxiliaryXhair[axh_id]);
65
66     if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
67     {
68         axh                     = spawn();
69         axh.cnt                 = axh_id;
70         axh.drawonlytoclient    = own;
71         axh.owner               = own;
72         Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair);
73     }
74
75     setorigin(axh, loc);
76     axh.colormod            = clr;
77     axh.SendFlags           = 0x01;
78     own.(AuxiliaryXhair[axh_id]) = axh;
79 }
80
81 /*
82 // SVC_TEMPENTITY based, horrible with even 50 ping. hm.
83 // WriteByte(MSG_ONE, SVC_TEMPENTITY) uses reliable messagess, never use for thinsg that need continous updates.
84 void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id)
85 {
86         msgexntity = own;
87
88         WriteByte(MSG_ONE, SVC_TEMPENTITY);
89         WriteByte(MSG_ONE, TE_CSQC_AUXILIARYXHAIR);
90
91         WriteByte(MSG_ONE, axh_id);
92
93         WriteCoord(MSG_ONE, loc_x);
94         WriteCoord(MSG_ONE, loc_y);
95         WriteCoord(MSG_ONE, loc_z);
96
97     WriteByte(MSG_ONE, rint(clr_x * 255));
98     WriteByte(MSG_ONE, rint(clr_y * 255));
99     WriteByte(MSG_ONE, rint(clr_z * 255));
100
101 }
102 */
103 // End AuxiliaryXhair
104
105 /**
106     Notifies the client that he enterd a vehicle, and sends
107     realavent data.
108
109     only sends vehicle_id atm (wich is a HUD_* constant, ex. HUD_SPIDERBOT)
110 **/
111 void CSQCVehicleSetup(entity own, float vehicle_id)
112 {
113     if (!IS_REAL_CLIENT(own))
114         return;
115
116         msg_entity = own;
117
118         WriteByte(MSG_ONE, SVC_TEMPENTITY);
119         WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP);
120         if(vehicle_id != 0)
121             WriteByte(MSG_ONE, vehicle_id);
122         else
123         WriteByte(MSG_ONE, 1 + own.vehicle.vehicle_weapon2mode + HUD_VEHICLE_LAST);
124 }
125
126 /** vehicles_locktarget
127
128     Generic target locking.
129
130     Figure out if what target is "locked" (if any), for missile tracking as such.
131
132     after calling, "if(self.lock_target != world && self.lock_strength == 1)" mean
133     you have a locked in target.
134
135     Exspects a crosshair_trace() or equivalent to be
136     dont before calling.
137
138 **/
139 .entity lock_target;
140 .float  lock_strength;
141 .float  lock_time;
142 .float  lock_soundtime;
143 const float     DAMAGE_TARGETDRONE = 10;
144
145 vector targetdrone_getnewspot()
146 {
147
148         vector spot;
149         float i;
150         for(i = 0; i < 100; ++i)
151         {
152                 spot = self.origin + randomvec() * 1024;
153                 tracebox(spot, self.mins, self.maxs, spot, MOVE_NORMAL, self);
154                 if(trace_fraction == 1.0 && trace_startsolid == 0 && trace_allsolid == 0)
155                         return spot;
156         }
157         return self.origin;
158 }
159
160 #if 0
161 void targetdrone_think();
162 void targetdrone_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
163 void targetdrone_renwe()
164 {
165         self.think = targetdrone_think;
166         self.nextthink = time + 0.1;
167         setorigin(self, targetdrone_getnewspot());
168         self.health = 200;
169         self.takedamage = DAMAGE_TARGETDRONE;
170         self.event_damage = targetdrone_damage;
171         self.solid = SOLID_BBOX;
172         setmodel(self, "models/runematch/rune.mdl");
173         self.effects = EF_LOWPRECISION;
174         self.scale = 10;
175         self.movetype = MOVETYPE_BOUNCEMISSILE;
176         setsize(self, '-100 -100 -100', '100 100 100');
177
178 }
179 void targetdrone_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
180 {
181         self.health -= damage;
182         if(self.health <= 0)
183         {
184                 pointparticles(particleeffectnum("explosion_medium"), self.origin, '0 0 0', 1);
185
186                 if(!self.cnt)
187                         remove(self);
188                 else
189                 {
190                         self.think = targetdrone_renwe;
191                         self.nextthink = time + 1 + random() * 2;
192                         self.solid = SOLID_NOT;
193                         setmodel(self, "");
194                 }
195         }
196 }
197 entity targetdrone_getfear()
198 {
199         entity fear;
200         float i;
201
202         for(i = 64; i <= 1024; i += 64)
203         {
204                 fear = findradius(self.origin, i);
205                 while(fear)
206                 {
207                         if(fear.bot_dodge)
208                                 return fear;
209
210                         fear = fear.chain;
211                 }
212         }
213
214         return world;
215 }
216 void targetdrone_think()
217 {
218         self.nextthink = time + 0.1;
219
220         if(self.wp00)
221         if(self.wp00.deadflag != DEAD_NO)
222                 self.wp00 = targetdrone_getfear();
223
224         if(!self.wp00)
225                 self.wp00 = targetdrone_getfear();
226
227         vector newdir;
228
229         if(self.wp00)
230                 newdir = steerlib_push(self.wp00.origin) + randomvec() * 0.75;
231         else
232                 newdir = randomvec() * 0.75;
233
234         newdir = newdir * 0.5 + normalize(self.velocity) * 0.5;
235
236         if(self.wp00)
237                 self.velocity = normalize(newdir) * (500 + (1024 / min(vlen(self.wp00.origin - self.origin), 1024)) * 700);
238         else
239                 self.velocity = normalize(newdir) * 750;
240
241         tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 2, MOVE_NORMAL, self);
242         if(trace_fraction != 1.0)
243                 self.velocity = self.velocity * -1;
244
245         //normalize((normalize(self.velocity) * 0.5 + newdir * 0.5)) * 750;
246 }
247
248 void targetdrone_spawn(vector _where, float _autorenew)
249 {
250         entity drone = spawn();
251         setorigin(drone, _where);
252         drone.think = targetdrone_renwe;
253         drone.nextthink = time + 0.1;
254         drone.cnt = _autorenew;
255 }
256 #endif
257
258 void vehicles_locktarget(float incr, float decr, float _lock_time)
259 {
260     if(self.lock_target && self.lock_target.deadflag != DEAD_NO)
261     {
262         self.lock_target    = world;
263         self.lock_strength  = 0;
264         self.lock_time      = 0;
265     }
266
267     if(self.lock_time > time)
268     {
269         if(self.lock_target)
270         if(self.lock_soundtime < time)
271         {
272             self.lock_soundtime = time + 0.5;
273             play2(self.owner, "vehicles/locked.wav");
274         }
275
276         return;
277     }
278
279     if(trace_ent != world)
280     {
281         if(teamplay && trace_ent.team == self.team)
282             trace_ent = world;
283
284         if(trace_ent.deadflag != DEAD_NO)
285             trace_ent = world;
286         if(!(
287             (trace_ent.vehicle_flags & VHF_ISVEHICLE) || 
288             (trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET) || 
289             (trace_ent.takedamage == DAMAGE_TARGETDRONE)
290             )) { trace_ent = world; }
291     }
292
293     if(self.lock_target == world && trace_ent != world)
294         self.lock_target = trace_ent;
295
296     if(self.lock_target && trace_ent == self.lock_target)
297     {
298         if(self.lock_strength != 1 && self.lock_strength + incr >= 1)
299         {
300             play2(self.owner, "vehicles/lock.wav");
301             self.lock_soundtime = time + 0.8;
302         }
303         else if (self.lock_strength != 1 && self.lock_soundtime < time)
304         {
305             play2(self.owner, "vehicles/locking.wav");
306             self.lock_soundtime = time + 0.3;
307         }
308
309     }
310
311     // Have a locking target
312     // Trace hit current target
313     if(trace_ent == self.lock_target && trace_ent != world)
314     {
315         self.lock_strength = min(self.lock_strength + incr, 1);
316         if(self.lock_strength == 1)
317             self.lock_time = time + _lock_time;
318     }
319     else
320     {
321         if(trace_ent)
322             self.lock_strength = max(self.lock_strength - decr * 2, 0);
323         else
324             self.lock_strength = max(self.lock_strength - decr, 0);
325
326         if(self.lock_strength == 0)
327             self.lock_target = world;
328     }
329 }
330
331 #define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \
332 ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100
333
334 #define vehicles_sweap_collision(orig,vel,dt,acm,mult) \
335 traceline(orig, orig + vel * dt, MOVE_NORMAL, self); \
336 if(trace_fraction != 1) \
337     acm += normalize(self.origin - trace_endpos) * (vlen(vel) * mult)
338
339 // Hover movement support
340 float  force_fromtag_power;
341 float  force_fromtag_normpower;
342 vector force_fromtag_origin;
343 vector vehicles_force_fromtag_hover(string tag_name, float spring_length, float max_power)
344 {
345     force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
346     v_forward  = normalize(v_forward) * -1;
347     traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
348
349     force_fromtag_power = (1 - trace_fraction) * max_power;
350     force_fromtag_normpower = force_fromtag_power / max_power;
351
352     return v_forward  * force_fromtag_power;
353 }
354
355 // Experimental hovermode wich uses attraction/repulstion from surface insted of gravity/repulsion
356 // Can possibly be use to move abt any surface (inclusing walls/celings)
357 vector vehicles_force_fromtag_maglev(string tag_name, float spring_length, float max_power)
358 {
359
360     force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
361     v_forward  = normalize(v_forward) * -1;
362     traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
363
364     // TODO - this may NOT be compatible with wall/celing movement, unhardcode 0.25 (engine count multiplier)
365     if(trace_fraction == 1.0)
366     {
367         force_fromtag_normpower = -0.25;
368         return '0 0 -200';
369     }
370
371     force_fromtag_power = ((1 - trace_fraction) - trace_fraction) * max_power;
372     force_fromtag_normpower = force_fromtag_power / max_power;
373
374     return v_forward  * force_fromtag_power;
375 }
376
377 // Generic vehile projectile system
378 void vehicles_projectile_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
379 {
380     // Ignore damage from oterh projectiles from my owner (dont mess up volly's)
381     if(inflictor.owner == self.owner)
382         return;
383
384     self.health -= damage;
385     self.velocity += force;
386     if(self.health < 1)
387     {
388         self.takedamage = DAMAGE_NO;
389         self.event_damage = func_null;
390         self.think = self.use;
391         self.nextthink = time;
392     }
393 }
394
395 void vehicles_projectile_explode()
396 {
397     if(self.owner && other != world)
398     {
399         if(other == self.owner.vehicle)
400             return;
401
402         if(other == self.owner.vehicle.tur_head)
403             return;
404     }
405
406         PROJECTILE_TOUCH;
407
408         self.event_damage = func_null;
409     RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other);
410
411     remove (self);
412 }
413
414 entity vehicles_projectile(string _mzlfx, string _mzlsound,
415                            vector _org, vector _vel,
416                            float _dmg, float _radi, float _force,  float _size,
417                            float _deahtype, float _projtype, float _health,
418                            float _cull, float _clianim, entity _owner)
419 {
420     entity proj;
421
422     proj = spawn();
423
424     PROJECTILE_MAKETRIGGER(proj);
425     setorigin(proj, _org);
426
427     proj.shot_dmg         = _dmg;
428     proj.shot_radius      = _radi;
429     proj.shot_force       = _force;
430     proj.totalfrags       = _deahtype;
431     proj.solid            = SOLID_BBOX;
432     proj.movetype         = MOVETYPE_FLYMISSILE;
433     proj.flags            = FL_PROJECTILE;
434     proj.bot_dodge        = true;
435     proj.bot_dodgerating  = _dmg;
436     proj.velocity         = _vel;
437     proj.touch            = vehicles_projectile_explode;
438     proj.use              = vehicles_projectile_explode;
439     proj.owner            = self;
440     proj.realowner        = _owner;
441     proj.think            = SUB_Remove;
442     proj.nextthink        = time + 30;
443
444     if(_health)
445     {
446         proj.takedamage       = DAMAGE_AIM;
447         proj.event_damage     = vehicles_projectile_damage;
448         proj.health           = _health;
449     }
450     else
451         proj.flags           = FL_PROJECTILE | FL_NOTARGET;
452
453     if(_mzlsound)
454         sound (self, CH_WEAPON_A, _mzlsound, VOL_BASE, ATTEN_NORM);
455
456     if(_mzlfx)
457         pointparticles(particleeffectnum(_mzlfx), proj.origin, proj.velocity, 1);
458
459
460     setsize (proj, '-1 -1 -1' * _size, '1 1 1' * _size);
461
462     CSQCProjectile(proj, _clianim, _projtype, _cull);
463
464     return proj;
465 }
466 // End generic vehile projectile system
467
468 void vehicles_reset()
469 {
470         if(self.owner)
471         {
472                 entity oldself = self;
473                 self = self.owner;
474                 vehicles_exit(VHEF_RELESE);
475                 self = oldself;
476         }
477         self.alpha      = -1;
478         self.movetype   = MOVETYPE_NONE;
479         self.effects    = EF_NODRAW;
480         self.colormod  = '0 0 0';
481         self.avelocity = '0 0 0';
482         self.velocity  = '0 0 0';
483         self.event_damage = func_null;
484         self.solid = SOLID_NOT;
485         self.deadflag = DEAD_NO;
486
487         self.touch = func_null;
488         self.nextthink = 0;
489         vehicles_setreturn();
490 }
491
492 /** vehicles_spawn
493     Exetuted for all vehicles on (re)spawn.
494     Sets defaults for newly spawned units.
495 **/
496 void vehicles_spawn()
497 {
498     dprint("Spawning vehicle: ", self.netname, "\n");
499
500     // De-own & reset
501     self.vehicle_hudmodel.viewmodelforclient = self;
502
503     self.owner              = world;
504     self.touch              = vehicles_touch;
505     self.event_damage       = vehicles_damage;
506     self.reset              = vehicles_reset;
507     self.iscreature         = true;
508     self.teleportable       = false; // no teleporting for vehicles, too buggy
509     self.damagedbycontents      = true;
510     self.movetype           = MOVETYPE_WALK;
511     self.solid              = SOLID_SLIDEBOX;
512     self.takedamage         = DAMAGE_AIM;
513         self.deadflag           = DEAD_NO;
514     self.bot_attack         = true;
515     self.flags              = FL_NOTARGET;
516     self.avelocity          = '0 0 0';
517     self.velocity           = '0 0 0';
518
519     // Reset locking
520     self.lock_strength      = 0;
521     self.lock_target        = world;
522     self.misc_bulletcounter = 0;
523
524     // Return to spawn
525     self.angles             = self.pos2;
526     setorigin(self, self.pos1 + '0 0 0');
527     // Show it
528     pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
529
530     if(self.vehicle_controller)
531         self.team = self.vehicle_controller.team;
532
533     vehicles_reset_colors();
534     self.vehicle_spawn(VHSF_NORMAL);
535 }
536
537 // Better way of determening whats crushable needed! (fl_crushable?)
538 float vehicles_crushable(entity e)
539 {
540     if(IS_PLAYER(e))
541         return true;
542
543     if(e.flags & FL_MONSTER)
544         return true;
545
546     return false;
547 }
548
549 void vehicles_impact(float _minspeed, float _speedfac, float _maxpain)
550 {
551     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
552         return;
553
554     if(self.play_time < time)
555     {
556         float wc = vlen(self.velocity - self.oldvelocity);
557         //dprint("oldvel: ", vtos(self.oldvelocity), "\n");
558         //dprint("vel: ", vtos(self.velocity), "\n");
559         if(_minspeed < wc)
560         {
561             float take = min(_speedfac * wc, _maxpain);
562             Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0');
563             self.play_time = time + 0.25;
564
565             //dprint("wc: ", ftos(wc), "\n");
566             //dprint("take: ", ftos(take), "\n");
567         }
568     }
569 }
570
571 .void() vehicle_impact;
572 void vehicles_touch()
573 {
574         if(MUTATOR_CALLHOOK(VehicleTouch))
575                 return;
576
577     // Vehicle currently in use
578     if(self.owner)
579     {
580         if(other != world)
581         if(vehicles_crushable(other))
582         {
583             if(vlen(self.velocity) != 0)
584                 Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force);
585
586             return; // Dont do selfdamage when hitting "soft targets".
587         }
588
589         if(self.play_time < time)
590         if(self.vehicle_impact)
591             self.vehicle_impact();
592
593         return;
594     }
595
596     if (!IS_PLAYER(other))
597         return;
598
599     if(other.deadflag != DEAD_NO)
600         return;
601
602     if(other.vehicle != world)
603         return;
604
605     vehicles_enter();
606 }
607 float autocvar_g_vehicles_allow_bots = 0;
608 void vehicles_enter()
609 {
610    // Remove this when bots know how to use vehicles
611
612     if (IS_BOT_CLIENT(other))
613         if (autocvar_g_vehicles_allow_bots)
614             dprint("Bot enters vehicle\n"); // This is where we need to disconnect (some, all?) normal bot AI and hand over to vehicle's _aiframe()
615         else
616             return;
617
618     if(self.phase > time)
619         return;
620     if(other.frozen)
621         return;
622     if(other.vehicle)
623         return;
624     if(other.deadflag != DEAD_NO)
625         return;
626
627     if(teamplay)
628     if(self.team)
629     if(self.team != other.team)
630         return;
631
632     RemoveGrapplingHook(other);
633
634     self.vehicle_ammo1   = 0;
635     self.vehicle_ammo2   = 0;
636     self.vehicle_reload1 = 0;
637     self.vehicle_reload2 = 0;
638     self.vehicle_energy  = 0;
639
640     self.owner          = other;
641     self.switchweapon   = other.switchweapon;
642
643     // .viewmodelforclient works better.
644     //self.vehicle_hudmodel.drawonlytoclient = self.owner;
645
646     self.vehicle_hudmodel.viewmodelforclient = self.owner;
647
648     self.event_damage         = vehicles_damage;
649     self.nextthink            = 0;
650     self.owner.angles         = self.angles;
651     self.owner.takedamage     = DAMAGE_NO;
652     self.owner.solid          = SOLID_NOT;
653     self.owner.movetype       = MOVETYPE_NOCLIP;
654     self.owner.alpha          = -1;
655     self.owner.vehicle        = self;
656     self.owner.event_damage   = func_null;
657     self.owner.view_ofs       = '0 0 0';
658     self.colormap             = self.owner.colormap;
659     if(self.tur_head)
660         self.tur_head.colormap    = self.owner.colormap;
661
662     self.owner.hud            = self.hud;
663     self.owner.PlayerPhysplug = self.PlayerPhysplug;
664
665     self.owner.vehicle_ammo1    = self.vehicle_ammo1;
666     self.owner.vehicle_ammo2    = self.vehicle_ammo2;
667     self.owner.vehicle_reload1  = self.vehicle_reload1;
668     self.owner.vehicle_reload2  = self.vehicle_reload2;
669
670     // Cant do this, hides attached objects too.
671     //self.exteriormodeltoclient = self.owner;
672     //self.tur_head.exteriormodeltoclient = self.owner;
673
674     other.flags &= ~FL_ONGROUND;
675     self.flags  &= ~FL_ONGROUND;
676
677     self.team                 = self.owner.team;
678     self.flags               -= FL_NOTARGET;
679     self.monster_attack       = true;
680
681     if (IS_REAL_CLIENT(other))
682     {
683         msg_entity = other;
684         WriteByte (MSG_ONE, SVC_SETVIEWPORT);
685         WriteEntity(MSG_ONE, self.vehicle_viewport);
686
687         WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
688         if(self.tur_head)
689         {
690             WriteAngle(MSG_ONE, self.tur_head.angles.x + self.angles.x); // tilt
691             WriteAngle(MSG_ONE, self.tur_head.angles.y + self.angles.y); // yaw
692             WriteAngle(MSG_ONE, 0);                                      // roll
693         }
694         else
695         {
696             WriteAngle(MSG_ONE,  self.angles.x * -1); // tilt
697             WriteAngle(MSG_ONE,  self.angles.y);      // yaw
698             WriteAngle(MSG_ONE,  0);                  // roll
699         }
700     }
701
702     vehicles_clearreturn();
703
704     CSQCVehicleSetup(self.owner, self.hud);
705
706     vh_player = other;
707     vh_vehicle = self;
708     MUTATOR_CALLHOOK(VehicleEnter);
709     other = vh_player;
710     self = vh_vehicle;
711
712     self.vehicle_enter();
713     antilag_clear(other);
714 }
715
716 /** vehicles_findgoodexit
717     Locates a valid location for the player to exit the vehicle.
718     Will first try prefer_spot, then up 100 random spots arround the vehicle
719     wich are in direct line of sight and empty enougth to hold a players bbox
720 **/
721 vector vehicles_findgoodexit(vector prefer_spot)
722 {
723     //vector exitspot;
724     float mysize;
725
726     tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, self.owner);
727     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
728         return prefer_spot;
729
730     mysize = 1.5 * vlen(self.maxs - self.mins);
731     float i;
732     vector v, v2;
733     v2 = 0.5 * (self.absmin + self.absmax);
734     for(i = 0; i < 100; ++i)
735     {
736         v = randomvec();
737         v.z = 0;
738         v = v2 + normalize(v) * mysize;
739         tracebox(v2, PL_MIN, PL_MAX, v, MOVE_NORMAL, self.owner);
740         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
741             return v;
742     }
743
744     /*
745     exitspot = (self.origin + '0 0 48') + v_forward * mysize;
746     tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
747     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
748         return exitspot;
749
750     exitspot = (self.origin + '0 0 48') - v_forward * mysize;
751     tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
752     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
753         return exitspot;
754
755     exitspot = (self.origin + '0 0 48') + v_right * mysize;
756     tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
757     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
758         return exitspot;
759
760     exitspot = (self.origin + '0 0 48') - v_right * mysize;
761     tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
762     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
763         return exitspot;
764     */
765
766     return self.origin;
767 }
768
769 /** vehicles_exit
770     Standarrd vehicle release fucntion.
771     custom code goes in self.vehicle_exit
772 **/
773 float vehicles_exit_running;
774 void vehicles_exit(float eject)
775 {
776     entity _vehicle;
777     entity _player;
778     entity _oldself = self;
779
780     if(vehicles_exit_running)
781     {
782         dprint("^1vehicles_exit allready running! this is not good..\n");
783         return;
784     }
785
786     vehicles_exit_running = true;
787     if(IS_CLIENT(self))
788     {
789         _vehicle = self.vehicle;
790
791         if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
792         {
793             _vehicle.vehicle_exit(eject);
794             self = _oldself;
795             vehicles_exit_running = false;
796             return;
797         }
798     }
799     else
800         _vehicle = self;
801
802     _player = _vehicle.owner;
803
804     self = _vehicle;
805
806     if (_player)
807     {
808         if (IS_REAL_CLIENT(_player))
809         {
810             msg_entity = _player;
811             WriteByte (MSG_ONE, SVC_SETVIEWPORT);
812             WriteEntity( MSG_ONE, _player);
813
814             WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
815             WriteAngle(MSG_ONE, 0);
816             WriteAngle(MSG_ONE, _vehicle.angles.y);
817             WriteAngle(MSG_ONE, 0);
818         }
819
820         setsize(_player, PL_MIN,PL_MAX);
821
822         _player.takedamage     = DAMAGE_AIM;
823         _player.solid          = SOLID_SLIDEBOX;
824         _player.movetype       = MOVETYPE_WALK;
825         _player.effects        &= ~EF_NODRAW;
826         _player.alpha          = 1;
827         _player.PlayerPhysplug = func_null;
828         _player.vehicle        = world;
829         _player.view_ofs       = PL_VIEW_OFS;
830         _player.event_damage   = PlayerDamage;
831         _player.hud            = HUD_NORMAL;
832         _player.switchweapon   = _vehicle.switchweapon;
833
834         CSQCVehicleSetup(_player, HUD_NORMAL);
835     }
836     _vehicle.flags |= FL_NOTARGET;
837
838     if(_vehicle.deadflag == DEAD_NO)
839         _vehicle.avelocity          = '0 0 0';
840
841     _vehicle.tur_head.nodrawtoclient             = world;
842
843     if(!teamplay)
844         _vehicle.team = 0;
845
846     vh_player = _player;
847     vh_vehicle = _vehicle;
848     MUTATOR_CALLHOOK(VehicleExit);
849     _player = vh_player;
850     _vehicle = vh_vehicle;
851
852     _vehicle.team = _vehicle.tur_head.team;
853
854     sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTEN_NORM);
855     _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;
856     _vehicle.phase = time + 1;
857     _vehicle.monster_attack = false;
858
859     _vehicle.vehicle_exit(eject);
860
861     vehicles_setreturn();
862     vehicles_reset_colors();
863     _vehicle.owner = world;
864     self = _oldself;
865
866     vehicles_exit_running = false;
867 }
868
869
870 void vehicles_regen(float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale)
871 {
872     if (self.(regen_field) < field_max)
873     if (timer + rpause < time)
874     {
875         if (_healthscale)
876             regen = regen * (self.vehicle_health / self.tur_health);
877
878         self.(regen_field) = min(self.(regen_field) + regen * delta_time, field_max);
879
880         if (self.owner)
881             self.owner.(regen_field) = (self.(regen_field) / field_max) * 100;
882     }
883 }
884
885 void shieldhit_think()
886 {
887     self.alpha -= 0.1;
888     if (self.alpha <= 0)
889     {
890         //setmodel(self, "");
891         self.alpha = -1;
892         self.effects |= EF_NODRAW;
893     }
894     else
895     {
896         self.nextthink = time + 0.1;
897     }
898 }
899
900 void vehicles_painframe()
901 {
902     if(self.owner.vehicle_health <= 50)
903     if(self.pain_frame < time)
904     {
905         float _ftmp;
906         _ftmp = self.owner.vehicle_health / 50;
907         self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp);
908         pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
909
910         if(self.vehicle_flags & VHF_DMGSHAKE)
911             self.velocity += randomvec() * 30;
912
913         if(self.vehicle_flags & VHF_DMGROLL)
914             if(self.vehicle_flags & VHF_DMGHEADROLL)
915                 self.tur_head.angles += randomvec();
916             else
917                 self.angles += randomvec();
918
919     }
920 }
921
922 void vehicles_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
923 {
924     self.dmg_time = time;
925
926         // WEAPONTODO
927     if(DEATH_ISWEAPON(deathtype, WEP_VORTEX))
928         damage *= autocvar_g_vehicles_vortex_damagerate;
929
930     if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
931         damage *= autocvar_g_vehicles_machinegun_damagerate;
932
933     if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
934         damage *= autocvar_g_vehicles_rifle_damagerate;
935
936     if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
937         damage *= autocvar_g_vehicles_vaporizer_damagerate;
938
939     if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
940         damage *= autocvar_g_vehicles_tag_damagerate;
941
942     self.enemy = attacker;
943
944     if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0))
945     {
946         if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world)
947         {
948             self.vehicle_shieldent = spawn();
949             self.vehicle_shieldent.effects = EF_LOWPRECISION;
950
951             setmodel(self.vehicle_shieldent, "models/vhshield.md3");
952             setattachment(self.vehicle_shieldent, self, "");
953             setorigin(self.vehicle_shieldent, real_origin(self) - self.origin);
954             self.vehicle_shieldent.scale       = 256 / vlen(self.maxs - self.mins);
955             self.vehicle_shieldent.think       = shieldhit_think;
956         }
957
958         self.vehicle_shieldent.colormod    = '1 1 1';
959         self.vehicle_shieldent.alpha       = 0.45;
960         self.vehicle_shieldent.angles      = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles;
961         self.vehicle_shieldent.nextthink   = time;
962         self.vehicle_shieldent.effects &= ~EF_NODRAW;
963
964         self.vehicle_shield -= damage;
965
966         if(self.vehicle_shield < 0)
967         {
968             self.vehicle_health            -= fabs(self.vehicle_shield);
969             self.vehicle_shieldent.colormod = '2 0 0';
970             self.vehicle_shield             = 0;
971             self.vehicle_shieldent.alpha    = 0.75;
972
973                 if(sound_allowed(MSG_BROADCAST, attacker))
974                 spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);   // FIXME: PLACEHOLDER
975         }
976         else
977                 if(sound_allowed(MSG_BROADCAST, attacker))
978                 spamsound (self, CH_PAIN, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
979
980     }
981     else
982     {
983         self.vehicle_health -= damage;
984
985         if(sound_allowed(MSG_BROADCAST, attacker))
986             spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
987     }
988
989         if(self.damageforcescale < 1 && self.damageforcescale > 0)
990                 self.velocity += force * self.damageforcescale;
991         else
992                 self.velocity += force;
993
994     if(self.vehicle_health <= 0)
995     {
996         if(self.owner)
997             if(self.vehicle_flags & VHF_DEATHEJECT)
998                 vehicles_exit(VHEF_EJECT);
999             else
1000                 vehicles_exit(VHEF_RELESE);
1001
1002
1003         antilag_clear(self);
1004
1005         self.vehicle_die();
1006         vehicles_setreturn();
1007     }
1008 }
1009
1010 void vehicles_clearreturn()
1011 {
1012     entity ret;
1013     // Remove "return helper", if any.
1014     ret = findchain(classname, "vehicle_return");
1015     while(ret)
1016     {
1017         if(ret.wp00 == self)
1018         {
1019             ret.classname   = "";
1020             ret.think       = SUB_Remove;
1021             ret.nextthink   = time + 0.1;
1022
1023             if(ret.waypointsprite_attached)
1024                 WaypointSprite_Kill(ret.waypointsprite_attached);
1025
1026             return;
1027         }
1028         ret = ret.chain;
1029     }
1030 }
1031
1032 void vehicles_return()
1033 {
1034     pointparticles(particleeffectnum("teleport"), self.wp00.origin + '0 0 64', '0 0 0', 1);
1035
1036     self.wp00.think     = vehicles_spawn;
1037     self.wp00.nextthink = time;
1038
1039     if(self.waypointsprite_attached)
1040         WaypointSprite_Kill(self.waypointsprite_attached);
1041
1042     remove(self);
1043 }
1044
1045 void vehicles_showwp_goaway()
1046 {
1047     if(self.waypointsprite_attached)
1048         WaypointSprite_Kill(self.waypointsprite_attached);
1049
1050     remove(self);
1051
1052 }
1053
1054 void vehicles_showwp()
1055 {
1056     entity oldself = world;
1057     vector rgb;
1058
1059     if(self.cnt)
1060     {
1061         self.think      = vehicles_return;
1062         self.nextthink  = self.cnt;
1063     }
1064     else
1065     {
1066         self.think      = vehicles_return;
1067         self.nextthink  = time +1;
1068
1069         oldself = self;
1070         self = spawn();
1071         setmodel(self, "null");
1072         self.team = oldself.wp00.team;
1073         self.wp00 = oldself.wp00;
1074         setorigin(self, oldself.wp00.pos1);
1075
1076         self.nextthink = time + 5;
1077         self.think = vehicles_showwp_goaway;
1078     }
1079
1080     if(teamplay && self.team)
1081             rgb = Team_ColorRGB(self.team);
1082     else
1083             rgb = '1 1 1';
1084     WaypointSprite_Spawn("vehicle", 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP, rgb);
1085     if(self.waypointsprite_attached)
1086     {
1087         WaypointSprite_UpdateRule(self.waypointsprite_attached, self.wp00.team, SPRITERULE_DEFAULT);
1088         if(oldself == world)
1089             WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, self.nextthink);
1090         WaypointSprite_Ping(self.waypointsprite_attached);
1091     }
1092
1093     if(oldself != world)
1094         self = oldself;
1095 }
1096
1097 void vehicles_setreturn()
1098 {
1099     entity ret;
1100
1101     vehicles_clearreturn();
1102
1103     ret = spawn();
1104     ret.classname   = "vehicle_return";
1105     ret.wp00       = self;
1106     ret.team        = self.team;
1107     ret.think       = vehicles_showwp;
1108
1109         if(self.deadflag != DEAD_NO)
1110         {
1111                 ret.cnt = max(game_starttime, time) + self.vehicle_respawntime;
1112                 ret.nextthink = max(game_starttime, time) + max(0, self.vehicle_respawntime - 5);
1113         }
1114         else
1115                 ret.nextthink = max(game_starttime, time) + max(0, self.vehicle_respawntime - 1);
1116
1117     setmodel(ret, "null");
1118     setorigin(ret, self.pos1 + '0 0 96');
1119 }
1120
1121 void vehicles_reset_colors()
1122 {
1123     entity e;
1124     float _effects = 0, _colormap;
1125     vector _glowmod, _colormod;
1126
1127     if(autocvar_g_nodepthtestplayers)
1128         _effects |= EF_NODEPTHTEST;
1129
1130     if(autocvar_g_fullbrightplayers)
1131         _effects |= EF_FULLBRIGHT;
1132
1133     if(self.team)
1134         _colormap = 1024 + (self.team - 1) * 17;
1135     else
1136         _colormap = 1024;
1137
1138     _glowmod  = '0 0 0';
1139     _colormod = '0 0 0';
1140
1141     // Find all ents attacked to main model and setup effects, colormod etc.
1142     e = findchainentity(tag_entity, self);
1143     while(e)
1144     {
1145         if(e != self.vehicle_shieldent)
1146         {
1147             e.effects   = _effects; //  | EF_LOWPRECISION;
1148             e.colormod  = _colormod;
1149             e.colormap  = _colormap;
1150             e.alpha     = 1;
1151         }
1152         e = e.chain;
1153     }
1154
1155     self.vehicle_hudmodel.effects  = self.effects  = _effects; // | EF_LOWPRECISION;
1156     self.vehicle_hudmodel.colormod = self.colormod = _colormod;
1157     self.vehicle_hudmodel.colormap = self.colormap = _colormap;
1158     self.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
1159
1160     self.alpha     = 1;
1161     self.avelocity = '0 0 0';
1162     self.velocity  = '0 0 0';
1163     self.effects   = _effects;
1164 }
1165
1166 void vehicle_use()
1167 {
1168     dprint("vehicle ",self.netname, " used by ", activator.classname, "\n");
1169
1170     self.tur_head.team = activator.team;
1171
1172     if(self.tur_head.team == 0)
1173         self.active = ACTIVE_NOT;
1174     else
1175         self.active = ACTIVE_ACTIVE;
1176
1177     if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO)
1178     {
1179         dprint("^3Eat shit yall!\n");
1180         vehicles_setreturn();
1181         vehicles_reset_colors();
1182     }
1183     else if(self.active == ACTIVE_NOT && self.deadflag != DEAD_NO)
1184     {
1185
1186     }
1187 }
1188
1189 float vehicle_addplayerslot(    entity _owner,
1190                                 entity _slot,
1191                                 float _hud,
1192                                 string _hud_model,
1193                                 float() _framefunc,
1194                                 void(float) _exitfunc)
1195 {
1196     if (!(_owner.vehicle_flags & VHF_MULTISLOT))
1197         _owner.vehicle_flags |= VHF_MULTISLOT;
1198
1199     _slot.PlayerPhysplug = _framefunc;
1200     _slot.vehicle_exit = _exitfunc;
1201     _slot.hud = _hud;
1202     _slot.vehicle_flags = VHF_PLAYERSLOT;
1203     _slot.vehicle_viewport = spawn();
1204     _slot.vehicle_hudmodel = spawn();
1205     _slot.vehicle_hudmodel.viewmodelforclient = _slot;
1206     _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
1207
1208     setmodel(_slot.vehicle_hudmodel, _hud_model);
1209     setmodel(_slot.vehicle_viewport, "null");
1210
1211     setattachment(_slot.vehicle_hudmodel, _slot, "");
1212     setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, "");
1213
1214     return true;
1215 }
1216
1217 float vehicle_initialize(string  net_name,
1218                          string  bodymodel,
1219                          string  topmodel,
1220                          string  hudmodel,
1221                          string  toptag,
1222                          string  hudtag,
1223                          string  viewtag,
1224                          float   vhud,
1225                          vector  min_s,
1226                          vector  max_s,
1227                          float   nodrop,
1228                          void(float _spawnflag)  spawnproc,
1229                          float   _respawntime,
1230                          float() physproc,
1231                          void()  enterproc,
1232                          void(float extflag) exitfunc,
1233                          void() dieproc,
1234                          void() thinkproc,
1235                          float  use_csqc,
1236                          float _max_health,
1237                          float _max_shield)
1238 {
1239         if(!autocvar_g_vehicles)
1240                 return false;
1241
1242     if(self.targetname)
1243     {
1244         self.vehicle_controller = find(world, target, self.targetname);
1245         if(!self.vehicle_controller)
1246         {
1247             bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
1248         }
1249         else
1250         {
1251             self.team = self.vehicle_controller.team;
1252             self.use = vehicle_use;
1253
1254             if(teamplay)
1255             {
1256                 if(self.vehicle_controller.team == 0)
1257                     self.active = ACTIVE_NOT;
1258                 else
1259                     self.active = ACTIVE_ACTIVE;
1260             }
1261         }
1262     }
1263
1264     precache_sound("onslaught/ons_hit2.wav");
1265     precache_sound("onslaught/electricity_explode.wav");
1266
1267
1268     addstat(STAT_HUD, AS_INT,  hud);
1269         addstat(STAT_VEHICLESTAT_HEALTH,  AS_INT, vehicle_health);
1270         addstat(STAT_VEHICLESTAT_SHIELD,  AS_INT, vehicle_shield);
1271         addstat(STAT_VEHICLESTAT_ENERGY,  AS_INT, vehicle_energy);
1272
1273         addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT, vehicle_ammo1);
1274         addstat(STAT_VEHICLESTAT_RELOAD1, AS_INT, vehicle_reload1);
1275
1276         addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT, vehicle_ammo2);
1277         addstat(STAT_VEHICLESTAT_RELOAD2, AS_INT, vehicle_reload2);
1278
1279     if(bodymodel == "")
1280         error("vehicles: missing bodymodel!");
1281
1282     if(hudmodel == "")
1283         error("vehicles: missing hudmodel!");
1284
1285     if(net_name == "")
1286         self.netname = self.classname;
1287     else
1288         self.netname = net_name;
1289
1290     if(self.team && !teamplay)
1291         self.team = 0;
1292
1293     self.vehicle_flags |= VHF_ISVEHICLE;
1294
1295     setmodel(self, bodymodel);
1296
1297     self.vehicle_viewport   = spawn();
1298     self.vehicle_hudmodel   = spawn();
1299     self.tur_head           = spawn();
1300     self.tur_head.owner     = self;
1301     self.takedamage         = DAMAGE_AIM;
1302     self.bot_attack         = true;
1303     self.iscreature         = true;
1304     self.teleportable       = false; // no teleporting for vehicles, too buggy
1305     self.damagedbycontents      = true;
1306     self.hud                = vhud;
1307     self.tur_health          = _max_health;
1308     self.tur_head.tur_health = _max_shield;
1309     self.vehicle_die         = dieproc;
1310     self.vehicle_exit        = exitfunc;
1311     self.vehicle_enter       = enterproc;
1312     self.PlayerPhysplug      = physproc;
1313     self.event_damage        = func_null;
1314     self.touch               = vehicles_touch;
1315     self.think               = vehicles_spawn;
1316     self.vehicle_spawn       = spawnproc;
1317         self.vehicle_respawntime = max(0, _respawntime);
1318     self.effects             = EF_NODRAW;
1319         self.dphitcontentsmask   = DPCONTENTS_BODY | DPCONTENTS_SOLID;
1320         if(!autocvar_g_vehicles_delayspawn || !self.vehicle_respawntime)
1321                 self.nextthink = time;
1322         else
1323                 self.nextthink = max(time, game_starttime) + max(0, self.vehicle_respawntime + ((random() * 2 - 1) * autocvar_g_vehicles_delayspawn_jitter));
1324
1325         if(autocvar_g_playerclip_collisions)
1326                 self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
1327
1328     if(autocvar_g_nodepthtestplayers)
1329         self.effects = self.effects | EF_NODEPTHTEST;
1330
1331     if(autocvar_g_fullbrightplayers)
1332         self.effects = self.effects | EF_FULLBRIGHT;
1333
1334     setmodel(self.vehicle_hudmodel, hudmodel);
1335     setmodel(self.vehicle_viewport, "null");
1336
1337     if(topmodel != "")
1338     {
1339         setmodel(self.tur_head, topmodel);
1340         setattachment(self.tur_head, self, toptag);
1341         setattachment(self.vehicle_hudmodel, self.tur_head, hudtag);
1342         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
1343     }
1344     else
1345     {
1346         setattachment(self.tur_head, self, "");
1347         setattachment(self.vehicle_hudmodel, self, hudtag);
1348         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
1349     }
1350
1351     setsize(self, min_s, max_s);
1352     if (!nodrop)
1353     {
1354         setorigin(self, self.origin);
1355         tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
1356         setorigin(self, trace_endpos);
1357     }
1358
1359     self.pos1 = self.origin;
1360     self.pos2 = self.angles;
1361     self.tur_head.team = self.team;
1362
1363         if(MUTATOR_CALLHOOK(VehicleSpawn))
1364                 return false;
1365
1366     return true;
1367 }
1368
1369 vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname,
1370                          float _pichlimit_min, float _pichlimit_max,
1371                          float _rotlimit_min, float _rotlimit_max, float _aimspeed)
1372 {
1373     vector vtmp, vtag;
1374     float ftmp;
1375     vtag = gettaginfo(_turrret, gettagindex(_turrret, _tagname));
1376     vtmp = vectoangles(normalize(_target - vtag));
1377     vtmp = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(_vehic.angles), AnglesTransform_FromAngles(vtmp))) - _turrret.angles;
1378     vtmp = AnglesTransform_Normalize(vtmp, true);
1379     ftmp = _aimspeed * frametime;
1380     vtmp.y = bound(-ftmp, vtmp.y, ftmp);
1381     vtmp.x = bound(-ftmp, vtmp.x, ftmp);
1382     _turrret.angles_y = bound(_rotlimit_min, _turrret.angles.y + vtmp.y, _rotlimit_max);
1383     _turrret.angles_x = bound(_pichlimit_min, _turrret.angles.x + vtmp.x, _pichlimit_max);
1384     return vtag;
1385 }
1386
1387 void vehicles_gib_explode()
1388 {
1389         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
1390         pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
1391         remove(self);
1392 }
1393
1394 void vehicles_gib_think()
1395 {
1396         self.alpha -= 0.1;
1397         if(self.cnt >= time)
1398                 remove(self);
1399         else
1400                 self.nextthink = time + 0.1;
1401 }
1402
1403 entity vehicle_tossgib(entity _template, vector _vel, string _tag, float _burn, float _explode, float _maxtime, vector _rot)
1404 {
1405         entity _gib = spawn();
1406         setmodel(_gib, _template.model);
1407         setorigin(_gib, gettaginfo(self, gettagindex(self, _tag)));
1408         _gib.velocity = _vel;
1409         _gib.movetype = MOVETYPE_TOSS;
1410         _gib.solid = SOLID_CORPSE;
1411         _gib.colormod = '-0.5 -0.5 -0.5';
1412         _gib.effects = EF_LOWPRECISION;
1413         _gib.avelocity = _rot;
1414
1415         if(_burn)
1416                 _gib.effects |= EF_FLAME;
1417
1418         if(_explode)
1419         {
1420                 _gib.think = vehicles_gib_explode;
1421                 _gib.nextthink = time + random() * _explode;
1422                 _gib.touch = vehicles_gib_explode;
1423         }
1424         else
1425         {
1426                 _gib.cnt = time + _maxtime;
1427                 _gib.think = vehicles_gib_think;
1428                 _gib.nextthink = time + _maxtime - 1;
1429                 _gib.alpha = 1;
1430         }
1431         return _gib;
1432 }
1433
1434 /*
1435 vector predict_target(entity _targ, vector _from, float _shot_speed)
1436 {
1437     float i;                // loop
1438     float _distance;        // How far to target
1439     float _impact_time;     // How long untill projectile impacts
1440     vector _predict_pos;    // Predicted enemy location
1441     vector _original_origin;// Where target is before predicted
1442
1443      _original_origin = real_origin(_targ); // Typicaly center of target BBOX
1444
1445     _predict_pos = _original_origin;
1446     for(i = 0; i < 4; ++i)  // Loop a few times to increase prediction accuracy (increase loop count if accuracy is to low)
1447     {
1448         _distance = vlen(_predict_pos - _from); // Get distance to previos predicted location
1449         _impact_time = _distance / _shot_speed; // Calculate impact time
1450         _predict_pos = _original_origin + _targ.velocity * _impact_time; // Calculate new predicted location
1451     }
1452
1453     return _predict_pos;
1454 }
1455 */