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