]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/vehicles/sv_vehicles.qc
Some minor cleanup
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / sv_vehicles.qc
1 #include "vehicles.qh"
2 #include "sv_vehicles.qh"
3
4 float SendAuxiliaryXhair(entity to, float sf)
5 {
6
7         WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
8
9         WriteByte(MSG_ENTITY, self.cnt);
10
11         WriteCoord(MSG_ENTITY, self.origin_x);
12         WriteCoord(MSG_ENTITY, self.origin_y);
13         WriteCoord(MSG_ENTITY, self.origin_z);
14
15         WriteByte(MSG_ENTITY, rint(self.colormod_x * 255));
16         WriteByte(MSG_ENTITY, rint(self.colormod_y * 255));
17         WriteByte(MSG_ENTITY, rint(self.colormod_z * 255));
18
19         return true;
20 }
21
22 void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id)
23 {
24         if(!IS_REAL_CLIENT(own))
25                 return;
26
27         entity axh;
28
29         axh_id = bound(0, axh_id, MAX_AXH);
30         axh = own.(AuxiliaryXhair[axh_id]);
31
32         if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
33         {
34                 axh                                      = spawn();
35                 axh.cnt                          = axh_id;
36                 axh.drawonlytoclient    = own;
37                 axh.owner                          = own;
38                 Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair);
39         }
40
41         setorigin(axh, loc);
42         axh.colormod                    = clr;
43         axh.SendFlags              = 0x01;
44         own.(AuxiliaryXhair[axh_id]) = axh;
45 }
46
47 void CSQCVehicleSetup(entity own, float vehicle_id)
48 {
49         if(!IS_REAL_CLIENT(own))
50                 return;
51
52         msg_entity = own;
53
54         WriteByte(MSG_ONE, SVC_TEMPENTITY);
55         WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP);
56         WriteByte(MSG_ONE, vehicle_id);
57 }
58
59 vector targetdrone_getnewspot()
60 {
61         vector spot;
62         float i;
63         for(i = 0; i < 100; ++i)
64         {
65                 spot = self.origin + randomvec() * 1024;
66                 tracebox(spot, self.mins, self.maxs, spot, MOVE_NORMAL, self);
67                 if(trace_fraction == 1.0 && trace_startsolid == 0 && trace_allsolid == 0)
68                         return spot;
69         }
70         return self.origin;
71 }
72
73 void vehicles_locktarget(float incr, float decr, float _lock_time)
74 {
75         if(self.lock_target && self.lock_target.deadflag != DEAD_NO)
76         {
77                 self.lock_target        = world;
78                 self.lock_strength  = 0;
79                 self.lock_time    = 0;
80         }
81
82         if(self.lock_time > time)
83         {
84                 if(self.lock_target)
85                 if(self.lock_soundtime < time)
86                 {
87                         self.lock_soundtime = time + 0.5;
88                         play2(self.owner, "vehicles/locked.wav");
89                 }
90
91                 return;
92         }
93
94         if(trace_ent != world)
95         {
96                 if(SAME_TEAM(trace_ent, self))
97                         trace_ent = world;
98
99                 if(trace_ent.deadflag != DEAD_NO)
100                         trace_ent = world;
101
102                 if(!((trace_ent.vehicle_flags & VHF_ISVEHICLE) || (trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET)))
103                         trace_ent = world;
104
105                 if(trace_ent.alpha <= 0.5 && trace_ent.alpha != 0)
106                         trace_ent = world; // invisible
107         }
108
109         if(self.lock_target == world && trace_ent != world)
110                 self.lock_target = trace_ent;
111
112         if(self.lock_target && trace_ent == self.lock_target)
113         {
114                 if(self.lock_strength != 1 && self.lock_strength + incr >= 1)
115                 {
116                         play2(self.owner, "vehicles/lock.wav");
117                         self.lock_soundtime = time + 0.8;
118                 }
119                 else if (self.lock_strength != 1 && self.lock_soundtime < time)
120                 {
121                         play2(self.owner, "vehicles/locking.wav");
122                         self.lock_soundtime = time + 0.3;
123                 }
124         }
125
126         // Have a locking target
127         // Trace hit current target
128         if(trace_ent == self.lock_target && trace_ent != world)
129         {
130                 self.lock_strength = min(self.lock_strength + incr, 1);
131                 if(self.lock_strength == 1)
132                         self.lock_time = time + _lock_time;
133         }
134         else
135         {
136                 if(trace_ent)
137                         self.lock_strength = max(self.lock_strength - decr * 2, 0);
138                 else
139                         self.lock_strength = max(self.lock_strength - decr, 0);
140
141                 if(self.lock_strength == 0)
142                         self.lock_target = world;
143         }
144 }
145
146 vector vehicles_force_fromtag_hover(string tag_name, float spring_length, float max_power)
147 {
148         force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
149         v_forward  = normalize(v_forward) * -1;
150         traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
151
152         force_fromtag_power = (1 - trace_fraction) * max_power;
153         force_fromtag_normpower = force_fromtag_power / max_power;
154
155         return v_forward  * force_fromtag_power;
156 }
157
158 vector vehicles_force_fromtag_maglev(string tag_name, float spring_length, float max_power)
159 {
160
161         force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
162         v_forward  = normalize(v_forward) * -1;
163         traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
164
165         // TODO - this may NOT be compatible with wall/celing movement, unhardcode 0.25 (engine count multiplier)
166         if(trace_fraction == 1.0)
167         {
168                 force_fromtag_normpower = -0.25;
169                 return '0 0 -200';
170         }
171
172         force_fromtag_power = ((1 - trace_fraction) - trace_fraction) * max_power;
173         force_fromtag_normpower = force_fromtag_power / max_power;
174
175         return v_forward  * force_fromtag_power;
176 }
177
178 // projectile handling
179 void vehicles_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
180 {
181         // Ignore damage from oterh projectiles from my owner (dont mess up volly's)
182         if(inflictor.owner == self.owner)
183                 return;
184
185         self.health -= damage;
186         self.velocity += force;
187         if(self.health < 1)
188         {
189                 self.takedamage = DAMAGE_NO;
190                 self.event_damage = func_null;
191                 self.think = self.use;
192                 self.nextthink = time;
193         }
194 }
195
196 void vehicles_projectile_explode()
197 {
198         if(self.owner && other != world)
199         {
200                 if(other == self.owner.vehicle)
201                         return;
202
203                 if(other == self.owner.vehicle.tur_head)
204                         return;
205         }
206
207         PROJECTILE_TOUCH;
208
209         self.event_damage = func_null;
210         RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other);
211
212         remove (self);
213 }
214
215 entity vehicles_projectile(string _mzlfx, string _mzlsound,
216                                                    vector _org, vector _vel,
217                                                    float _dmg, float _radi, float _force,  float _size,
218                                                    float _deahtype, float _projtype, float _health,
219                                                    float _cull, float _clianim, entity _owner)
220 {
221         entity proj;
222
223         proj = spawn();
224
225         PROJECTILE_MAKETRIGGER(proj);
226         setorigin(proj, _org);
227
228         proj.shot_dmg            = _dmg;
229         proj.shot_radius          = _radi;
230         proj.shot_force    = _force;
231         proj.totalfrags    = _deahtype;
232         proj.solid                      = SOLID_BBOX;
233         proj.movetype            = MOVETYPE_FLYMISSILE;
234         proj.flags                      = FL_PROJECTILE;
235         proj.bot_dodge          = true;
236         proj.bot_dodgerating  = _dmg;
237         proj.velocity            = _vel;
238         proj.touch                      = vehicles_projectile_explode;
239         proj.use                          = vehicles_projectile_explode;
240         proj.owner                      = self;
241         proj.realowner          = _owner;
242         proj.think                      = SUB_Remove;
243         proj.nextthink          = time + 30;
244
245         if(_health)
246         {
247                 proj.takedamage    = DAMAGE_AIM;
248                 proj.event_damage        = vehicles_projectile_damage;
249                 proj.health                = _health;
250         }
251         else
252                 proj.flags                 = FL_PROJECTILE | FL_NOTARGET;
253
254         if(_mzlsound)
255                 sound (self, CH_WEAPON_A, _mzlsound, VOL_BASE, ATTEN_NORM);
256
257         if(_mzlfx)
258                 pointparticles(particleeffectnum(_mzlfx), proj.origin, proj.velocity, 1);
259
260
261         setsize (proj, '-1 -1 -1' * _size, '1 1 1' * _size);
262
263         CSQCProjectile(proj, _clianim, _projtype, _cull);
264
265         return proj;
266 }
267
268 void vehicles_gib_explode()
269 {
270         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
271         pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
272         pointparticles(particleeffectnum("explosion_small"), self.wp00.origin + '0 0 64', '0 0 0', 1);
273         remove(self);
274 }
275
276 void vehicles_gib_think()
277 {
278         self.alpha -= 0.1;
279         if(self.cnt >= time)
280                 remove(self);
281         else
282                 self.nextthink = time + 0.1;
283 }
284
285 entity vehicle_tossgib(entity _template, vector _vel, string _tag, float _burn, float _explode, float _maxtime, vector _rot)
286 {
287         entity _gib = spawn();
288         setmodel(_gib, _template.model);
289         setorigin(_gib, gettaginfo(self, gettagindex(self, _tag)));
290         _gib.velocity = _vel;
291         _gib.movetype = MOVETYPE_TOSS;
292         _gib.solid = SOLID_CORPSE;
293         _gib.colormod = '-0.5 -0.5 -0.5';
294         _gib.effects = EF_LOWPRECISION;
295         _gib.avelocity = _rot;
296
297         if(_burn)
298                 _gib.effects |= EF_FLAME;
299
300         if(_explode)
301         {
302                 _gib.think = vehicles_gib_explode;
303                 _gib.nextthink = time + random() * _explode;
304                 _gib.touch = vehicles_gib_explode;
305         }
306         else
307         {
308                 _gib.cnt = time + _maxtime;
309                 _gib.think = vehicles_gib_think;
310                 _gib.nextthink = time + _maxtime - 1;
311                 _gib.alpha = 1;
312         }
313         return _gib;
314 }
315
316 float vehicle_addplayerslot(    entity _owner,
317                                                                 entity _slot,
318                                                                 float _hud,
319                                                                 string _hud_model,
320                                                                 float() _framefunc,
321                                                                 void(float) _exitfunc, float() _enterfunc)
322 {
323         if(!(_owner.vehicle_flags & VHF_MULTISLOT))
324                 _owner.vehicle_flags |= VHF_MULTISLOT;
325
326         _slot.PlayerPhysplug = _framefunc;
327         _slot.vehicle_exit = _exitfunc;
328         _slot.vehicle_enter = _enterfunc;
329         _slot.hud = _hud;
330         _slot.vehicle_flags = VHF_PLAYERSLOT;
331         _slot.vehicle_viewport = spawn();
332         _slot.vehicle_hudmodel = spawn();
333         _slot.vehicle_hudmodel.viewmodelforclient = _slot;
334         _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
335
336         setmodel(_slot.vehicle_hudmodel, _hud_model);
337         setmodel(_slot.vehicle_viewport, "null");
338
339         setattachment(_slot.vehicle_hudmodel, _slot, "");
340         setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, "");
341
342         return true;
343 }
344
345 vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname,
346                                                  float _pichlimit_min, float _pichlimit_max,
347                                                  float _rotlimit_min, float _rotlimit_max, float _aimspeed)
348 {
349         vector vtmp, vtag;
350         float ftmp;
351         vtag = gettaginfo(_turrret, gettagindex(_turrret, _tagname));
352         vtmp = vectoangles(normalize(_target - vtag));
353         vtmp = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(_vehic.angles), AnglesTransform_FromAngles(vtmp))) - _turrret.angles;
354         vtmp = AnglesTransform_Normalize(vtmp, true);
355         ftmp = _aimspeed * frametime;
356         vtmp_y = bound(-ftmp, vtmp_y, ftmp);
357         vtmp_x = bound(-ftmp, vtmp_x, ftmp);
358         _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max);
359         _turrret.angles_x = bound(_pichlimit_min, _turrret.angles_x + vtmp_x, _pichlimit_max);
360         return vtag;
361 }
362
363 void vehicles_reset_colors()
364 {
365         entity e;
366         float _effects = 0, _colormap;
367         vector _glowmod, _colormod;
368
369         if(autocvar_g_nodepthtestplayers)
370                 _effects |= EF_NODEPTHTEST;
371
372         if(autocvar_g_fullbrightplayers)
373                 _effects |= EF_FULLBRIGHT;
374
375         if(self.team)
376                 _colormap = 1024 + (self.team - 1) * 17;
377         else
378                 _colormap = 1024;
379
380         _glowmod  = '0 0 0';
381         _colormod = '0 0 0';
382
383         // Find all ents attacked to main model and setup effects, colormod etc.
384         e = findchainentity(tag_entity, self);
385         while(e)
386         {
387                 if(e != self.vehicle_shieldent)
388                 {
389                         e.effects   = _effects; //  | EF_LOWPRECISION;
390                         e.colormod  = _colormod;
391                         e.colormap  = _colormap;
392                         e.alpha  = 1;
393                 }
394                 e = e.chain;
395         }
396         // Also check head tags
397         e = findchainentity(tag_entity, self.tur_head);
398         while(e)
399         {
400                 if(e != self.vehicle_shieldent)
401                 {
402                         e.effects   = _effects; //  | EF_LOWPRECISION;
403                         e.colormod  = _colormod;
404                         e.colormap  = _colormap;
405                         e.alpha  = 1;
406                 }
407                 e = e.chain;
408         }
409
410         self.vehicle_hudmodel.effects  = self.effects  = _effects; // | EF_LOWPRECISION;
411         self.vehicle_hudmodel.colormod = self.colormod = _colormod;
412         self.vehicle_hudmodel.colormap = self.colormap = _colormap;
413         self.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
414
415         self.alpha       = 1;
416         self.avelocity = '0 0 0';
417         self.velocity  = '0 0 0';
418         self.effects   = _effects;
419 }
420
421 void vehicles_clearreturn(entity veh)
422 {
423         entity ret;
424         // Remove "return helper", if any.
425         ret = findchain(classname, "vehicle_return");
426         while(ret)
427         {
428                 if(ret.wp00 == veh)
429                 {
430                         ret.classname   = "";
431                         ret.think          = SUB_Remove;
432                         ret.nextthink   = time + 0.1;
433
434                         if(ret.waypointsprite_attached)
435                                 WaypointSprite_Kill(ret.waypointsprite_attached);
436
437                         return;
438                 }
439                 ret = ret.chain;
440         }
441 }
442
443 void vehicles_spawn();
444 void vehicles_return()
445 {
446         pointparticles(particleeffectnum("teleport"), self.wp00.origin + '0 0 64', '0 0 0', 1);
447
448         self.wp00.think  = vehicles_spawn;
449         self.wp00.nextthink = time;
450
451         if(self.waypointsprite_attached)
452                 WaypointSprite_Kill(self.waypointsprite_attached);
453
454         remove(self);
455 }
456
457 void vehicles_showwp_goaway()
458 {
459         if(self.waypointsprite_attached)
460                 WaypointSprite_Kill(self.waypointsprite_attached);
461
462         remove(self);
463
464 }
465
466 void vehicles_showwp()
467 {
468         entity oldself = world;
469         vector rgb;
470
471         if(self.cnt)
472         {
473                 self.think        = vehicles_return;
474                 self.nextthink  = self.cnt;
475         }
476         else
477         {
478                 self.think        = vehicles_return;
479                 self.nextthink  = time +1;
480
481                 oldself = self;
482                 self = spawn();
483                 setmodel(self, "null");
484                 self.team = oldself.wp00.team;
485                 self.wp00 = oldself.wp00;
486                 setorigin(self, oldself.wp00.pos1);
487
488                 self.nextthink = time + 5;
489                 self.think = vehicles_showwp_goaway;
490         }
491
492         if(teamplay && self.team)
493                 rgb = Team_ColorRGB(self.team);
494         else
495                 rgb = '1 1 1';
496         WaypointSprite_Spawn("vehicle", 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP, rgb);
497         if(self.waypointsprite_attached)
498         {
499                 WaypointSprite_UpdateRule(self.waypointsprite_attached, self.wp00.team, SPRITERULE_DEFAULT);
500                 if(oldself == world)
501                         WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, self.nextthink);
502                 WaypointSprite_Ping(self.waypointsprite_attached);
503         }
504
505         if(oldself != world)
506                 self = oldself;
507 }
508
509 void vehicles_setreturn(entity veh)
510 {
511         entity ret;
512
513         vehicles_clearreturn(veh);
514
515         ret = spawn();
516         ret.classname   = "vehicle_return";
517         ret.wp00           = veh;
518         ret.team                = veh.team;
519         ret.think          = vehicles_showwp;
520
521         if(veh.deadflag != DEAD_NO)
522         {
523                 ret.cnt          = time + veh.respawntime;
524                 ret.nextthink   = min(time + veh.respawntime, time + veh.respawntime - 5);
525         }
526         else
527         {
528                 ret.nextthink   = min(time + veh.respawntime, time + veh.respawntime - 1);
529         }
530
531         setmodel(ret, "null");
532         setorigin(ret, veh.pos1 + '0 0 96');
533
534 }
535
536 void vehicle_use()
537 {
538         dprint("vehicle ",self.netname, " used by ", activator.classname, "\n");
539
540         self.tur_head.team = activator.team;
541
542         if(self.tur_head.team == 0)
543                 self.active = ACTIVE_NOT;
544         else
545                 self.active = ACTIVE_ACTIVE;
546
547         if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO && !gameover)
548         {
549                 dprint("Respawning vehicle: ", self.netname, "\n");
550                 if(self.effects & EF_NODRAW)
551                 {
552                         self.think = vehicles_spawn;
553                         self.nextthink = time + 3;
554                 }
555                 else
556                 {
557                         vehicles_setreturn(self);
558                         vehicles_reset_colors();
559                 }
560         }
561 }
562
563 void vehicles_regen(float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale)
564 {
565         if(self.regen_field < field_max)
566         if(timer + rpause < time)
567         {
568                 if(_healthscale)
569                         regen = regen * (self.vehicle_health / self.max_health);
570
571                 self.regen_field = min(self.regen_field + regen * delta_time, field_max);
572
573                 if(self.owner)
574                         self.owner.regen_field = (self.regen_field / field_max) * 100;
575         }
576 }
577
578 void shieldhit_think()
579 {
580         self.alpha -= 0.1;
581         if (self.alpha <= 0)
582         {
583                 //setmodel(self, "");
584                 self.alpha = -1;
585                 self.effects |= EF_NODRAW;
586         }
587         else
588         {
589                 self.nextthink = time + 0.1;
590         }
591 }
592
593 void vehicles_painframe()
594 {
595         if(self.owner.vehicle_health <= 50)
596         if(self.pain_frame < time)
597         {
598                 float _ftmp;
599                 _ftmp = self.owner.vehicle_health / 50;
600                 self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp);
601                 pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1);
602
603                 if(self.vehicle_flags & VHF_DMGSHAKE)
604                         self.velocity += randomvec() * 30;
605
606                 if(self.vehicle_flags & VHF_DMGROLL)
607                         if(self.vehicle_flags & VHF_DMGHEADROLL)
608                                 self.tur_head.angles += randomvec();
609                         else
610                                 self.angles += randomvec();
611
612         }
613 }
614
615 void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
616 {
617         self.dmg_time = time;
618
619         // WEAPONTODO
620         if(DEATH_ISWEAPON(deathtype, WEP_VORTEX))
621                 damage *= autocvar_g_vehicles_vortex_damagerate;
622
623         if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN))
624                 damage *= autocvar_g_vehicles_machinegun_damagerate;
625
626         if(DEATH_ISWEAPON(deathtype, WEP_RIFLE))
627                 damage *= autocvar_g_vehicles_rifle_damagerate;
628
629         if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER))
630                 damage *= autocvar_g_vehicles_vaporizer_damagerate;
631
632         if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
633                 damage *= autocvar_g_vehicles_tag_damagerate;
634
635         self.enemy = attacker;
636
637         self.pain_finished = time;
638
639         if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0))
640         {
641                 if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world)
642                 {
643                         self.vehicle_shieldent = spawn();
644                         self.vehicle_shieldent.effects = EF_LOWPRECISION;
645
646                         setmodel(self.vehicle_shieldent, "models/vhshield.md3");
647                         setattachment(self.vehicle_shieldent, self, "");
648                         setorigin(self.vehicle_shieldent, real_origin(self) - self.origin);
649                         self.vehicle_shieldent.scale       = 256 / vlen(self.maxs - self.mins);
650                         self.vehicle_shieldent.think       = shieldhit_think;
651                 }
652
653                 self.vehicle_shieldent.colormod = '1 1 1';
654                 self.vehicle_shieldent.alpha = 0.45;
655                 self.vehicle_shieldent.angles = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles;
656                 self.vehicle_shieldent.nextthink = time;
657                 self.vehicle_shieldent.effects &= ~EF_NODRAW;
658
659                 self.vehicle_shield -= damage;
660
661                 if(self.vehicle_shield < 0)
662                 {
663                         self.vehicle_health -= fabs(self.vehicle_shield);
664                         self.vehicle_shieldent.colormod = '2 0 0';
665                         self.vehicle_shield = 0;
666                         self.vehicle_shieldent.alpha = 0.75;
667
668                         if(sound_allowed(MSG_BROADCAST, attacker))
669                                 spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);   // FIXME: PLACEHOLDER
670                 }
671                 else
672                         if(sound_allowed(MSG_BROADCAST, attacker))
673                                 spamsound (self, CH_PAIN, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
674
675         }
676         else
677         {
678                 self.vehicle_health -= damage;
679
680                 if(sound_allowed(MSG_BROADCAST, attacker))
681                         spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
682         }
683
684         if(self.damageforcescale < 1 && self.damageforcescale > 0)
685                 self.velocity += force * self.damageforcescale;
686         else
687                 self.velocity += force;
688
689         if(self.vehicle_health <= 0)
690         {
691                 if(self.owner)
692                         if(self.vehicle_flags & VHF_DEATHEJECT)
693                                 vehicles_exit(VHEF_EJECT);
694                         else
695                                 vehicles_exit(VHEF_RELEASE);
696
697
698                 antilag_clear(self);
699
700                 VEH_ACTION(self.vehicleid, VR_DEATH);
701                 vehicles_setreturn(self);
702         }
703 }
704
705 float vehicles_crushable(entity e)
706 {
707         if(IS_PLAYER(e) && time >= e.vehicle_enter_delay)
708                 return true;
709
710         if(e.flags & FL_MONSTER)
711                 return true;
712
713         return false;
714 }
715
716 void vehicles_impact(float _minspeed, float _speedfac, float _maxpain)
717 {
718         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
719                 return;
720
721         if(self.play_time < time)
722         {
723                 float wc = vlen(self.velocity - self.oldvelocity);
724                 //dprint("oldvel: ", vtos(self.oldvelocity), "\n");
725                 //dprint("vel: ", vtos(self.velocity), "\n");
726                 if(_minspeed < wc)
727                 {
728                         float take = min(_speedfac * wc, _maxpain);
729                         Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0');
730                         self.play_time = time + 0.25;
731
732                         //dprint("wc: ", ftos(wc), "\n");
733                         //dprint("take: ", ftos(take), "\n");
734                 }
735         }
736 }
737
738 // vehicle enter/exit handling
739 vector vehicles_findgoodexit(vector prefer_spot)
740 {
741         //vector exitspot;
742         float mysize;
743
744         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, self.owner);
745         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
746                 return prefer_spot;
747
748         mysize = 1.5 * vlen(self.maxs - self.mins);
749         float i;
750         vector v, v2;
751         v2 = 0.5 * (self.absmin + self.absmax);
752         for(i = 0; i < 100; ++i)
753         {
754                 v = randomvec();
755                 v_z = 0;
756                 v = v2 + normalize(v) * mysize;
757                 tracebox(v2, PL_MIN, PL_MAX, v, MOVE_NORMAL, self.owner);
758                 if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
759                         return v;
760         }
761
762         /*
763         exitspot = (self.origin + '0 0 48') + v_forward * mysize;
764         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
765         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
766                 return exitspot;
767
768         exitspot = (self.origin + '0 0 48') - v_forward * mysize;
769         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
770         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
771                 return exitspot;
772
773         exitspot = (self.origin + '0 0 48') + v_right * mysize;
774         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
775         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
776                 return exitspot;
777
778         exitspot = (self.origin + '0 0 48') - v_right * mysize;
779         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
780         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
781                 return exitspot;
782         */
783
784         return self.origin;
785 }
786
787 void vehicles_exit(float eject)
788 {
789         entity _vehicle;
790         entity _player;
791         entity _oldself = self;
792
793         if(vehicles_exit_running)
794         {
795                 dprint("^1vehicles_exit allready running! this is not good..\n");
796                 return;
797         }
798
799         vehicles_exit_running = true;
800         if(IS_CLIENT(self))
801         {
802                 _vehicle = self.vehicle;
803
804                 if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
805                 {
806                         _vehicle.vehicle_exit(eject);
807                         self = _oldself;
808                         vehicles_exit_running = false;
809                         return;
810                 }
811         }
812         else
813                 _vehicle = self;
814
815         _player = _vehicle.owner;
816
817         self = _vehicle;
818
819         if (_player)
820         {
821                 if (IS_REAL_CLIENT(_player))
822                 {
823                         msg_entity = _player;
824                         WriteByte (MSG_ONE, SVC_SETVIEWPORT);
825                         WriteEntity( MSG_ONE, _player);
826
827                         WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
828                         WriteAngle(MSG_ONE, 0);
829                         WriteAngle(MSG_ONE, _vehicle.angles_y);
830                         WriteAngle(MSG_ONE, 0);
831                 }
832
833                 setsize(_player, PL_MIN,PL_MAX);
834
835                 _player.takedamage              = DAMAGE_AIM;
836                 _player.solid                   = SOLID_SLIDEBOX;
837                 _player.movetype                = MOVETYPE_WALK;
838                 _player.effects            &= ~EF_NODRAW;
839                 _player.teleportable    = TELEPORT_NORMAL;
840                 _player.alpha                   = 1;
841                 _player.PlayerPhysplug  = func_null;
842                 _player.vehicle                 = world;
843                 _player.view_ofs                = PL_VIEW_OFS;
844                 _player.event_damage    = PlayerDamage;
845                 _player.hud                             = HUD_NORMAL;
846                 _player.switchweapon    = _vehicle.switchweapon;
847                 _player.last_vehiclecheck = time + 3;
848                 _player.vehicle_enter_delay = time + 2;
849
850                 CSQCVehicleSetup(_player, HUD_NORMAL);
851         }
852         _vehicle.flags |= FL_NOTARGET;
853
854         if(_vehicle.deadflag == DEAD_NO)
855                 _vehicle.avelocity = '0 0 0';
856
857         _vehicle.tur_head.nodrawtoclient = world;
858
859         if(!teamplay)
860                 _vehicle.team = 0;
861
862         Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES);
863         Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES_OTHER); // kill all vehicle notifications when exiting a vehicle?
864
865         WaypointSprite_Kill(_vehicle.wps_intruder);
866
867         vh_player = _player;
868         vh_vehicle = _vehicle;
869         MUTATOR_CALLHOOK(VehicleExit);
870         _player = vh_player;
871         _vehicle = vh_vehicle;
872
873         _vehicle.team = _vehicle.tur_head.team;
874
875         sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTEN_NORM);
876         _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;
877         _vehicle.phase = time + 1;
878
879         _vehicle.vehicle_exit(eject);
880
881         vehicles_setreturn(_vehicle);
882         vehicles_reset_colors();
883         _vehicle.owner = world;
884
885         CSQCMODEL_AUTOINIT();
886
887         self = _oldself;
888
889         vehicles_exit_running = false;
890 }
891
892 void vehicles_touch()
893 {
894         if(MUTATOR_CALLHOOK(VehicleTouch))
895                 return;
896
897         // Vehicle currently in use
898         if(self.owner)
899         {
900                 if(!forbidWeaponUse(self.owner))
901                 if(other != world)
902                 if((self.origin_z + self.maxs_z) > (other.origin_z))
903                 if(vehicles_crushable(other))
904                 {
905                         if(vlen(self.velocity) >= 30)
906                                 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);
907
908                         return; // Dont do selfdamage when hitting "soft targets".
909                 }
910
911                 if(self.play_time < time)
912                         VEH_ACTION(self.vehicleid, VR_IMPACT);
913
914                 return;
915         }
916
917         if(autocvar_g_vehicles_enter)
918                 return;
919
920         vehicles_enter(other, self);
921 }
922
923 void vehicles_enter(entity pl, entity veh)
924 {
925    // Remove this when bots know how to use vehicles
926         if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots))
927                 return;
928
929         if((!IS_PLAYER(pl))
930         || (veh.phase >= time)
931         || (pl.vehicle_enter_delay >= time)
932         || (pl.frozen)
933         || (pl.deadflag != DEAD_NO)
934         || (pl.vehicle)
935         ) { return; }
936
937         if(autocvar_g_vehicles_enter) // vehicle's touch function should handle this if entering via use key is disabled (TODO)
938         if(veh.vehicle_flags & VHF_MULTISLOT)
939         if(veh.owner)
940         {
941                 entity oldself = self;
942                 self = veh;
943                 other = pl; // TODO: fix
944
945                 if(!veh.gunner1)
946                 if(time >= veh.gun1.phase)
947                 if(veh.gun1.vehicle_enter)
948                 if(veh.gun1.vehicle_enter())
949                 {
950                         self = oldself;
951                         return;
952                 }
953
954                 if(!veh.gunner2)
955                 if(time >= veh.gun2.phase)
956                 if(veh.gun2.vehicle_enter)
957                 if(veh.gun2.vehicle_enter())
958                 {
959                         self = oldself;
960                         return;
961                 }
962
963                 self = oldself;
964         }
965
966         if(teamplay)
967         if(veh.team)
968         if(DIFF_TEAM(pl, veh))
969         if(autocvar_g_vehicles_steal)
970         {
971                 entity head;
972                 FOR_EACH_PLAYER(head) if(SAME_TEAM(head, veh))
973                         Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_VEHICLE_STEAL);
974
975                 Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_STEAL_SELF);
976
977                 if(autocvar_g_vehicles_steal_show_waypoint)
978                         WaypointSprite_Spawn("intruder", 0, 0, pl, '0 0 68', world, veh.team, veh, wps_intruder, true, RADARICON_DANGER, Team_ColorRGB(pl.team));
979         }
980         else { return; }
981
982         RemoveGrapplingHook(pl);
983
984         veh.vehicle_ammo1 = 0;
985         veh.vehicle_ammo2 = 0;
986         veh.vehicle_reload1 = 0;
987         veh.vehicle_reload2 = 0;
988         veh.vehicle_energy = 0;
989
990         veh.owner = pl;
991         pl.vehicle = veh;
992
993         // .viewmodelforclient works better.
994         //veh.vehicle_hudmodel.drawonlytoclient = veh.owner;
995
996         veh.vehicle_hudmodel.viewmodelforclient = pl;
997
998         tracebox(pl.origin, PL_MIN, PL_MAX, pl.origin, false, pl);
999         pl.crouch = false;
1000         pl.view_ofs = PL_VIEW_OFS;
1001         setsize (pl, PL_MIN, PL_MAX);
1002
1003         veh.event_damage        = vehicles_damage;
1004         veh.nextthink           = 0;
1005         pl.angles                       = veh.angles;
1006         pl.takedamage           = DAMAGE_NO;
1007         pl.solid                        = SOLID_NOT;
1008         pl.movetype                     = MOVETYPE_NOCLIP;
1009         pl.teleportable         = false;
1010         pl.alpha                        = -1;
1011         pl.event_damage         = func_null;
1012         pl.view_ofs                     = '0 0 0';
1013         veh.colormap            = pl.colormap;
1014         if(veh.tur_head)
1015                 veh.tur_head.colormap = pl.colormap;
1016         veh.switchweapon = pl.switchweapon;
1017         pl.hud = veh.vehicleid;
1018         pl.PlayerPhysplug = veh.PlayerPhysplug;
1019
1020         pl.vehicle_ammo1 = veh.vehicle_ammo1;
1021         pl.vehicle_ammo2 = veh.vehicle_ammo2;
1022         pl.vehicle_reload1 = veh.vehicle_reload1;
1023         pl.vehicle_reload2 = veh.vehicle_reload2;
1024
1025         // Cant do this, hides attached objects too.
1026         //veh.exteriormodeltoclient = veh.owner;
1027         //veh.tur_head.exteriormodeltoclient = veh.owner;
1028
1029         pl.flags &= ~FL_ONGROUND;
1030         veh.flags &= ~FL_ONGROUND;
1031
1032         veh.team = pl.team;
1033         veh.flags -= FL_NOTARGET;
1034
1035         if (IS_REAL_CLIENT(pl))
1036         {
1037                 Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_ENTER);
1038
1039                 msg_entity = pl;
1040                 WriteByte (MSG_ONE, SVC_SETVIEWPORT);
1041                 WriteEntity(MSG_ONE, veh.vehicle_viewport);
1042
1043                 WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
1044                 if(veh.tur_head)
1045                 {
1046                         WriteAngle(MSG_ONE, veh.tur_head.angles_x + veh.angles_x); // tilt
1047                         WriteAngle(MSG_ONE, veh.tur_head.angles_y + veh.angles_y); // yaw
1048                         WriteAngle(MSG_ONE, 0);                                                                   // roll
1049                 }
1050                 else
1051                 {
1052                         WriteAngle(MSG_ONE, veh.angles_x * -1); // tilt
1053                         WriteAngle(MSG_ONE, veh.angles_y);        // yaw
1054                         WriteAngle(MSG_ONE, 0);                           // roll
1055                 }
1056         }
1057
1058         vehicles_clearreturn(veh);
1059
1060         CSQCVehicleSetup(pl, veh.vehicleid);
1061
1062         vh_player = pl;
1063         vh_vehicle = veh;
1064         MUTATOR_CALLHOOK(VehicleEnter);
1065
1066         entity oldself = self;
1067         self = veh;
1068         CSQCModel_UnlinkEntity();
1069         VEH_ACTION(veh.vehicleid, VR_ENTER);
1070         self = oldself;
1071
1072         antilag_clear(pl);
1073 }
1074
1075 void vehicles_think()
1076 {
1077         self.nextthink = time;
1078         
1079         if(self.owner)
1080                 self.owner.vehicle_weapon2mode = self.vehicle_weapon2mode;
1081         
1082         VEH_ACTION(self.vehicleid, VR_THINK);
1083
1084         CSQCMODEL_AUTOUPDATE();
1085 }
1086
1087 // initialization
1088 void vehicles_spawn()
1089 {
1090         dprint("Spawning vehicle: ", self.classname, "\n");
1091
1092         // disown & reset
1093         self.vehicle_hudmodel.viewmodelforclient = self;
1094
1095         self.owner                              = world;
1096         self.touch                              = vehicles_touch;
1097         self.event_damage               = vehicles_damage;
1098         self.iscreature                 = true;
1099         self.teleportable               = false; // no teleporting for vehicles, too buggy
1100         self.damagedbycontents  = true;
1101         self.movetype                   = MOVETYPE_WALK;
1102         self.solid                              = SOLID_SLIDEBOX;
1103         self.takedamage                 = DAMAGE_AIM;
1104         self.deadflag                   = DEAD_NO;
1105         self.bot_attack                 = true;
1106         self.flags                              = FL_NOTARGET;
1107         self.avelocity                  = '0 0 0';
1108         self.velocity                   = '0 0 0';
1109         self.think                              = vehicles_think;
1110         self.nextthink                  = time;
1111
1112         // Reset locking
1113         self.lock_strength = 0;
1114         self.lock_target = world;
1115         self.misc_bulletcounter = 0;
1116
1117         // Return to spawn
1118         self.angles = self.pos2;
1119         setorigin(self, self.pos1);
1120         // Show it
1121         pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
1122
1123         if(self.vehicle_controller)
1124                 self.team = self.vehicle_controller.team;
1125
1126         entity head; // remove hooks (if any)
1127         FOR_EACH_PLAYER(head)
1128         if(head.hook.aiment == self)
1129                 RemoveGrapplingHook(head);
1130
1131         vehicles_reset_colors();
1132
1133         VEH_ACTION(self.vehicleid, VR_SPAWN);
1134
1135         CSQCMODEL_AUTOINIT();
1136 }
1137
1138 float vehicle_initialize(float vehicle_id, float nodrop)
1139 {
1140         if(!autocvar_g_vehicles)
1141                 return false;
1142
1143         entity veh = get_vehicleinfo(vehicle_id);
1144
1145         if(!veh.vehicleid)
1146                 return false;
1147         
1148         if(!veh.tur_head) { VEH_ACTION(vehicle_id, VR_PRECACHE); }
1149
1150         if(self.targetname && self.targetname != "")
1151         {
1152                 self.vehicle_controller = find(world, target, self.targetname);
1153                 if(!self.vehicle_controller)
1154                 {
1155                         bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
1156                         self.active = ACTIVE_ACTIVE;
1157                 }
1158                 else
1159                 {
1160                         self.team = self.vehicle_controller.team;
1161                         self.use = vehicle_use;
1162
1163                         if(teamplay)
1164                         {
1165                                 if(self.vehicle_controller.team == 0)
1166                                         self.active = ACTIVE_NOT;
1167                                 else
1168                                         self.active = ACTIVE_ACTIVE;
1169                         }
1170                 }
1171         }
1172         else { self.active = ACTIVE_ACTIVE; }
1173
1174         if(self.team && (!teamplay || !autocvar_g_vehicles_teams))
1175                 self.team = 0;
1176
1177         self.vehicle_flags |= VHF_ISVEHICLE;
1178
1179         setmodel(self, veh.model);
1180
1181         self.vehicle_viewport           = spawn();
1182         self.vehicle_hudmodel           = spawn();
1183         self.tur_head                           = spawn();
1184         self.tur_head.owner                     = self;
1185         self.takedamage                         = DAMAGE_NO;
1186         self.bot_attack                         = true;
1187         self.iscreature                         = true;
1188         self.teleportable                       = false; // no teleporting for vehicles, too buggy
1189         self.damagedbycontents          = true;
1190         self.vehicleid                          = vehicle_id;
1191         self.PlayerPhysplug                     = veh.PlayerPhysplug;
1192         self.event_damage                       = func_null;
1193         self.touch                                      = vehicles_touch;
1194         self.think                                      = vehicles_spawn;
1195         self.nextthink                          = time;
1196         self.effects                            = EF_NODRAW;
1197         self.dphitcontentsmask          = DPCONTENTS_BODY | DPCONTENTS_SOLID;
1198
1199         if(autocvar_g_playerclip_collisions)
1200                 self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
1201
1202         if(autocvar_g_nodepthtestplayers)
1203                 self.effects |= EF_NODEPTHTEST;
1204
1205         if(autocvar_g_fullbrightplayers)
1206                 self.effects |= EF_FULLBRIGHT;
1207
1208         setmodel(self.vehicle_hudmodel, veh.hud_model);
1209         setmodel(self.vehicle_viewport, "null");
1210
1211         if(veh.head_model != "")
1212         {
1213                 setmodel(self.tur_head, veh.head_model);
1214                 setattachment(self.tur_head, self, veh.tag_head);
1215                 setattachment(self.vehicle_hudmodel, self.tur_head, veh.tag_hud);
1216                 setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view);
1217         }
1218         else
1219         {
1220                 setattachment(self.tur_head, self, "");
1221                 setattachment(self.vehicle_hudmodel, self, veh.tag_hud);
1222                 setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view);
1223         }
1224
1225         setsize(self, veh.mins, veh.maxs);
1226
1227         if(!nodrop)
1228         {
1229                 setorigin(self, self.origin);
1230                 tracebox(self.origin + '0 0 100', veh.mins, veh.maxs, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
1231                 setorigin(self, trace_endpos);
1232         }
1233
1234         self.pos1 = self.origin;
1235         self.pos2 = self.angles;
1236         self.tur_head.team = self.team;
1237
1238         VEH_ACTION(vehicle_id, VR_SETUP);
1239
1240         if(self.active == ACTIVE_NOT)
1241                 self.nextthink = 0; // wait until activated
1242         else if(autocvar_g_vehicles_delayspawn)
1243                 self.nextthink = time + self.respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter);
1244         else
1245                 self.nextthink = time + game_starttime;
1246
1247         if(MUTATOR_CALLHOOK(VehicleSpawn))
1248                 return false;
1249
1250         return true;
1251 }