]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/vehicles/sv_vehicles.qc
Merge new vehicle stuff from combined updates
[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         if(DEATH_WEAPONOFWEAPONDEATH(deathtype))
636                 damage *= autocvar_g_vehicles_weapon_damagerate;
637
638         self.enemy = attacker;
639
640         self.pain_finished = time;
641
642         if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0))
643         {
644                 if (wasfreed(self.vehicle_shieldent) || self.vehicle_shieldent == world)
645                 {
646                         self.vehicle_shieldent = spawn();
647                         self.vehicle_shieldent.effects = EF_LOWPRECISION;
648
649                         setmodel(self.vehicle_shieldent, "models/vhshield.md3");
650                         setattachment(self.vehicle_shieldent, self, "");
651                         setorigin(self.vehicle_shieldent, real_origin(self) - self.origin);
652                         self.vehicle_shieldent.scale       = 256 / vlen(self.maxs - self.mins);
653                         self.vehicle_shieldent.think       = shieldhit_think;
654                 }
655
656                 self.vehicle_shieldent.colormod = '1 1 1';
657                 self.vehicle_shieldent.alpha = 0.45;
658                 self.vehicle_shieldent.angles = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles;
659                 self.vehicle_shieldent.nextthink = time;
660                 self.vehicle_shieldent.effects &= ~EF_NODRAW;
661
662                 self.vehicle_shield -= damage;
663
664                 if(self.vehicle_shield < 0)
665                 {
666                         self.vehicle_health -= fabs(self.vehicle_shield);
667                         self.vehicle_shieldent.colormod = '2 0 0';
668                         self.vehicle_shield = 0;
669                         self.vehicle_shieldent.alpha = 0.75;
670
671                         if(sound_allowed(MSG_BROADCAST, attacker))
672                                 spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);   // FIXME: PLACEHOLDER
673                 }
674                 else
675                         if(sound_allowed(MSG_BROADCAST, attacker))
676                                 spamsound (self, CH_PAIN, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
677
678         }
679         else
680         {
681                 self.vehicle_health -= damage;
682
683                 if(sound_allowed(MSG_BROADCAST, attacker))
684                         spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);  // FIXME: PLACEHOLDER
685         }
686
687         if(self.damageforcescale < 1 && self.damageforcescale > 0)
688                 self.velocity += force * self.damageforcescale;
689         else
690                 self.velocity += force;
691
692         if(self.vehicle_health <= 0)
693         {
694                 if(self.owner)
695                         if(self.vehicle_flags & VHF_DEATHEJECT)
696                                 vehicles_exit(VHEF_EJECT);
697                         else
698                                 vehicles_exit(VHEF_RELEASE);
699
700
701                 antilag_clear(self);
702
703                 VEH_ACTION(self.vehicleid, VR_DEATH);
704                 vehicles_setreturn(self);
705         }
706 }
707
708 float vehicles_crushable(entity e)
709 {
710         if(IS_PLAYER(e) && time >= e.vehicle_enter_delay)
711                 return true;
712
713         if(e.flags & FL_MONSTER)
714                 return true;
715
716         return false;
717 }
718
719 void vehicles_impact(float _minspeed, float _speedfac, float _maxpain)
720 {
721         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
722                 return;
723
724         if(self.play_time < time)
725         {
726                 float wc = vlen(self.velocity - self.oldvelocity);
727                 //dprint("oldvel: ", vtos(self.oldvelocity), "\n");
728                 //dprint("vel: ", vtos(self.velocity), "\n");
729                 if(_minspeed < wc)
730                 {
731                         float take = min(_speedfac * wc, _maxpain);
732                         Damage (self, world, world, take, DEATH_FALL, self.origin, '0 0 0');
733                         self.play_time = time + 0.25;
734
735                         //dprint("wc: ", ftos(wc), "\n");
736                         //dprint("take: ", ftos(take), "\n");
737                 }
738         }
739 }
740
741 // vehicle enter/exit handling
742 vector vehicles_findgoodexit(vector prefer_spot)
743 {
744         //vector exitspot;
745         float mysize;
746
747         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, prefer_spot, MOVE_NORMAL, self.owner);
748         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
749                 return prefer_spot;
750
751         mysize = 1.5 * vlen(self.maxs - self.mins);
752         float i;
753         vector v, v2;
754         v2 = 0.5 * (self.absmin + self.absmax);
755         for(i = 0; i < 100; ++i)
756         {
757                 v = randomvec();
758                 v_z = 0;
759                 v = v2 + normalize(v) * mysize;
760                 tracebox(v2, PL_MIN, PL_MAX, v, MOVE_NORMAL, self.owner);
761                 if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
762                         return v;
763         }
764
765         /*
766         exitspot = (self.origin + '0 0 48') + v_forward * mysize;
767         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
768         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
769                 return exitspot;
770
771         exitspot = (self.origin + '0 0 48') - v_forward * mysize;
772         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
773         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
774                 return exitspot;
775
776         exitspot = (self.origin + '0 0 48') + v_right * mysize;
777         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
778         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
779                 return exitspot;
780
781         exitspot = (self.origin + '0 0 48') - v_right * mysize;
782         tracebox(self.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, self.owner);
783         if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
784                 return exitspot;
785         */
786
787         return self.origin;
788 }
789
790 void vehicles_exit(float eject)
791 {
792         entity _vehicle;
793         entity _player;
794         entity _oldself = self;
795
796         if(vehicles_exit_running)
797         {
798                 dprint("^1vehicles_exit allready running! this is not good..\n");
799                 return;
800         }
801
802         vehicles_exit_running = true;
803         if(IS_CLIENT(self))
804         {
805                 _vehicle = self.vehicle;
806
807                 if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
808                 {
809                         _vehicle.vehicle_exit(eject);
810                         self = _oldself;
811                         vehicles_exit_running = false;
812                         return;
813                 }
814         }
815         else
816                 _vehicle = self;
817
818         _player = _vehicle.owner;
819
820         self = _vehicle;
821
822         if (_player)
823         {
824                 if (IS_REAL_CLIENT(_player))
825                 {
826                         msg_entity = _player;
827                         WriteByte (MSG_ONE, SVC_SETVIEWPORT);
828                         WriteEntity( MSG_ONE, _player);
829
830                         WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
831                         WriteAngle(MSG_ONE, 0);
832                         WriteAngle(MSG_ONE, _vehicle.angles_y);
833                         WriteAngle(MSG_ONE, 0);
834                 }
835
836                 setsize(_player, PL_MIN,PL_MAX);
837
838                 _player.takedamage              = DAMAGE_AIM;
839                 _player.solid                   = SOLID_SLIDEBOX;
840                 _player.movetype                = MOVETYPE_WALK;
841                 _player.effects            &= ~EF_NODRAW;
842                 _player.teleportable    = TELEPORT_NORMAL;
843                 _player.alpha                   = 1;
844                 _player.PlayerPhysplug  = func_null;
845                 _player.vehicle                 = world;
846                 _player.view_ofs                = PL_VIEW_OFS;
847                 _player.event_damage    = PlayerDamage;
848                 _player.hud                             = HUD_NORMAL;
849                 _player.switchweapon    = _vehicle.switchweapon;
850                 _player.last_vehiclecheck = time + 3;
851                 _player.vehicle_enter_delay = time + 2;
852
853                 CSQCVehicleSetup(_player, HUD_NORMAL);
854         }
855         _vehicle.flags |= FL_NOTARGET;
856
857         if(_vehicle.deadflag == DEAD_NO)
858                 _vehicle.avelocity = '0 0 0';
859
860         _vehicle.tur_head.nodrawtoclient = world;
861
862         if(!teamplay)
863                 _vehicle.team = 0;
864
865         Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES);
866         Kill_Notification(NOTIF_ONE, _player, MSG_CENTER_CPID, CPID_VEHICLES_OTHER); // kill all vehicle notifications when exiting a vehicle?
867
868         WaypointSprite_Kill(_vehicle.wps_intruder);
869
870         vh_player = _player;
871         vh_vehicle = _vehicle;
872         MUTATOR_CALLHOOK(VehicleExit);
873         _player = vh_player;
874         _vehicle = vh_vehicle;
875
876         _vehicle.team = _vehicle.tur_head.team;
877
878         sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTEN_NORM);
879         _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;
880         _vehicle.phase = time + 1;
881
882         _vehicle.vehicle_exit(eject);
883
884         vehicles_setreturn(_vehicle);
885         vehicles_reset_colors();
886         _vehicle.owner = world;
887
888         CSQCMODEL_AUTOINIT();
889
890         self = _oldself;
891
892         vehicles_exit_running = false;
893 }
894
895 void vehicles_touch()
896 {
897         if(MUTATOR_CALLHOOK(VehicleTouch))
898                 return;
899
900         // Vehicle currently in use
901         if(self.owner)
902         {
903                 if(!forbidWeaponUse(self.owner))
904                 if(other != world)
905                 if((self.origin_z + self.maxs_z) > (other.origin_z))
906                 if(vehicles_crushable(other))
907                 {
908                         if(vlen(self.velocity) >= 30)
909                                 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);
910
911                         return; // Dont do selfdamage when hitting "soft targets".
912                 }
913
914                 if(self.play_time < time)
915                         VEH_ACTION(self.vehicleid, VR_IMPACT);
916
917                 return;
918         }
919
920         if(autocvar_g_vehicles_enter)
921                 return;
922
923         vehicles_enter(other, self);
924 }
925
926 void vehicles_enter(entity pl, entity veh)
927 {
928    // Remove this when bots know how to use vehicles
929         if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots))
930                 return;
931
932         if((!IS_PLAYER(pl))
933         || (veh.phase >= time)
934         || (pl.vehicle_enter_delay >= time)
935         || (pl.frozen)
936         || (pl.deadflag != DEAD_NO)
937         || (pl.vehicle)
938         ) { return; }
939
940         if(autocvar_g_vehicles_enter) // vehicle's touch function should handle this if entering via use key is disabled (TODO)
941         if(veh.vehicle_flags & VHF_MULTISLOT)
942         if(veh.owner)
943         {
944                 entity oldself = self;
945                 self = veh;
946                 other = pl; // TODO: fix
947
948                 if(!veh.gunner1)
949                 if(time >= veh.gun1.phase)
950                 if(veh.gun1.vehicle_enter)
951                 if(veh.gun1.vehicle_enter())
952                 {
953                         self = oldself;
954                         return;
955                 }
956
957                 if(!veh.gunner2)
958                 if(time >= veh.gun2.phase)
959                 if(veh.gun2.vehicle_enter)
960                 if(veh.gun2.vehicle_enter())
961                 {
962                         self = oldself;
963                         return;
964                 }
965
966                 self = oldself;
967         }
968
969         if(teamplay)
970         if(veh.team)
971         if(DIFF_TEAM(pl, veh))
972         if(autocvar_g_vehicles_steal)
973         {
974                 entity head;
975                 FOR_EACH_PLAYER(head) if(SAME_TEAM(head, veh))
976                         Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_VEHICLE_STEAL);
977
978                 Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_STEAL_SELF);
979
980                 if(autocvar_g_vehicles_steal_show_waypoint)
981                         WaypointSprite_Spawn("intruder", 0, 0, pl, '0 0 68', world, veh.team, veh, wps_intruder, true, RADARICON_DANGER, Team_ColorRGB(pl.team));
982         }
983         else { return; }
984
985         RemoveGrapplingHook(pl);
986
987         veh.vehicle_ammo1 = 0;
988         veh.vehicle_ammo2 = 0;
989         veh.vehicle_reload1 = 0;
990         veh.vehicle_reload2 = 0;
991         veh.vehicle_energy = 0;
992
993         veh.owner = pl;
994         pl.vehicle = veh;
995
996         // .viewmodelforclient works better.
997         //veh.vehicle_hudmodel.drawonlytoclient = veh.owner;
998
999         veh.vehicle_hudmodel.viewmodelforclient = pl;
1000
1001         tracebox(pl.origin, PL_MIN, PL_MAX, pl.origin, false, pl);
1002         pl.crouch = false;
1003         pl.view_ofs = PL_VIEW_OFS;
1004         setsize (pl, PL_MIN, PL_MAX);
1005
1006         veh.event_damage        = vehicles_damage;
1007         veh.nextthink           = 0;
1008         pl.angles                       = veh.angles;
1009         pl.takedamage           = DAMAGE_NO;
1010         pl.solid                        = SOLID_NOT;
1011         pl.movetype                     = MOVETYPE_NOCLIP;
1012         pl.teleportable         = false;
1013         pl.alpha                        = -1;
1014         pl.event_damage         = func_null;
1015         pl.view_ofs                     = '0 0 0';
1016         veh.colormap            = pl.colormap;
1017         if(veh.tur_head)
1018                 veh.tur_head.colormap = pl.colormap;
1019         veh.switchweapon = pl.switchweapon;
1020         pl.hud = veh.vehicleid;
1021         pl.PlayerPhysplug = veh.PlayerPhysplug;
1022
1023         pl.vehicle_ammo1 = veh.vehicle_ammo1;
1024         pl.vehicle_ammo2 = veh.vehicle_ammo2;
1025         pl.vehicle_reload1 = veh.vehicle_reload1;
1026         pl.vehicle_reload2 = veh.vehicle_reload2;
1027
1028         // Cant do this, hides attached objects too.
1029         //veh.exteriormodeltoclient = veh.owner;
1030         //veh.tur_head.exteriormodeltoclient = veh.owner;
1031
1032         pl.flags &= ~FL_ONGROUND;
1033         veh.flags &= ~FL_ONGROUND;
1034
1035         veh.team = pl.team;
1036         veh.flags -= FL_NOTARGET;
1037
1038         if (IS_REAL_CLIENT(pl))
1039         {
1040                 Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_VEHICLE_ENTER);
1041
1042                 msg_entity = pl;
1043                 WriteByte (MSG_ONE, SVC_SETVIEWPORT);
1044                 WriteEntity(MSG_ONE, veh.vehicle_viewport);
1045
1046                 WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
1047                 if(veh.tur_head)
1048                 {
1049                         WriteAngle(MSG_ONE, veh.tur_head.angles_x + veh.angles_x); // tilt
1050                         WriteAngle(MSG_ONE, veh.tur_head.angles_y + veh.angles_y); // yaw
1051                         WriteAngle(MSG_ONE, 0);                                                                   // roll
1052                 }
1053                 else
1054                 {
1055                         WriteAngle(MSG_ONE, veh.angles_x * -1); // tilt
1056                         WriteAngle(MSG_ONE, veh.angles_y);        // yaw
1057                         WriteAngle(MSG_ONE, 0);                           // roll
1058                 }
1059         }
1060
1061         vehicles_clearreturn(veh);
1062
1063         CSQCVehicleSetup(pl, veh.vehicleid);
1064
1065         vh_player = pl;
1066         vh_vehicle = veh;
1067         MUTATOR_CALLHOOK(VehicleEnter);
1068
1069         entity oldself = self;
1070         self = veh;
1071         CSQCModel_UnlinkEntity();
1072         VEH_ACTION(veh.vehicleid, VR_ENTER);
1073         self = oldself;
1074
1075         antilag_clear(pl);
1076 }
1077
1078 void vehicles_think()
1079 {
1080         self.nextthink = time;
1081         
1082         if(self.owner)
1083                 self.owner.vehicle_weapon2mode = self.vehicle_weapon2mode;
1084         
1085         VEH_ACTION(self.vehicleid, VR_THINK);
1086
1087         CSQCMODEL_AUTOUPDATE();
1088 }
1089
1090 // initialization
1091 void vehicles_spawn()
1092 {
1093         dprint("Spawning vehicle: ", self.classname, "\n");
1094
1095         // disown & reset
1096         self.vehicle_hudmodel.viewmodelforclient = self;
1097
1098         self.owner                              = world;
1099         self.touch                              = vehicles_touch;
1100         self.event_damage               = vehicles_damage;
1101         self.iscreature                 = true;
1102         self.teleportable               = false; // no teleporting for vehicles, too buggy
1103         self.damagedbycontents  = true;
1104         self.movetype                   = MOVETYPE_WALK;
1105         self.solid                              = SOLID_SLIDEBOX;
1106         self.takedamage                 = DAMAGE_AIM;
1107         self.deadflag                   = DEAD_NO;
1108         self.bot_attack                 = true;
1109         self.flags                              = FL_NOTARGET;
1110         self.avelocity                  = '0 0 0';
1111         self.velocity                   = '0 0 0';
1112         self.think                              = vehicles_think;
1113         self.nextthink                  = time;
1114
1115         // Reset locking
1116         self.lock_strength = 0;
1117         self.lock_target = world;
1118         self.misc_bulletcounter = 0;
1119
1120         // Return to spawn
1121         self.angles = self.pos2;
1122         setorigin(self, self.pos1);
1123         // Show it
1124         pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
1125
1126         if(self.vehicle_controller)
1127                 self.team = self.vehicle_controller.team;
1128
1129         entity head; // remove hooks (if any)
1130         FOR_EACH_PLAYER(head)
1131         if(head.hook.aiment == self)
1132                 RemoveGrapplingHook(head);
1133
1134         vehicles_reset_colors();
1135
1136         VEH_ACTION(self.vehicleid, VR_SPAWN);
1137
1138         CSQCMODEL_AUTOINIT();
1139 }
1140
1141 float vehicle_initialize(float vehicle_id, float nodrop)
1142 {
1143         if(!autocvar_g_vehicles)
1144                 return false;
1145
1146         entity veh = get_vehicleinfo(vehicle_id);
1147
1148         if(!veh.vehicleid)
1149                 return false;
1150         
1151         if(!veh.tur_head) { VEH_ACTION(vehicle_id, VR_PRECACHE); }
1152
1153         if(self.targetname && self.targetname != "")
1154         {
1155                 self.vehicle_controller = find(world, target, self.targetname);
1156                 if(!self.vehicle_controller)
1157                 {
1158                         bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
1159                         self.active = ACTIVE_ACTIVE;
1160                 }
1161                 else
1162                 {
1163                         self.team = self.vehicle_controller.team;
1164                         self.use = vehicle_use;
1165
1166                         if(teamplay)
1167                         {
1168                                 if(self.vehicle_controller.team == 0)
1169                                         self.active = ACTIVE_NOT;
1170                                 else
1171                                         self.active = ACTIVE_ACTIVE;
1172                         }
1173                 }
1174         }
1175         else { self.active = ACTIVE_ACTIVE; }
1176
1177         if(self.team && (!teamplay || !autocvar_g_vehicles_teams))
1178                 self.team = 0;
1179
1180         self.vehicle_flags |= VHF_ISVEHICLE;
1181
1182         setmodel(self, veh.model);
1183
1184         self.vehicle_viewport           = spawn();
1185         self.vehicle_hudmodel           = spawn();
1186         self.tur_head                           = spawn();
1187         self.tur_head.owner                     = self;
1188         self.takedamage                         = DAMAGE_NO;
1189         self.bot_attack                         = true;
1190         self.iscreature                         = true;
1191         self.teleportable                       = false; // no teleporting for vehicles, too buggy
1192         self.damagedbycontents          = true;
1193         self.vehicleid                          = vehicle_id;
1194         self.PlayerPhysplug                     = veh.PlayerPhysplug;
1195         self.event_damage                       = func_null;
1196         self.touch                                      = vehicles_touch;
1197         self.think                                      = vehicles_spawn;
1198         self.nextthink                          = time;
1199         self.effects                            = EF_NODRAW;
1200         self.dphitcontentsmask          = DPCONTENTS_BODY | DPCONTENTS_SOLID;
1201
1202         if(autocvar_g_playerclip_collisions)
1203                 self.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
1204
1205         if(autocvar_g_nodepthtestplayers)
1206                 self.effects |= EF_NODEPTHTEST;
1207
1208         if(autocvar_g_fullbrightplayers)
1209                 self.effects |= EF_FULLBRIGHT;
1210
1211         setmodel(self.vehicle_hudmodel, veh.hud_model);
1212         setmodel(self.vehicle_viewport, "null");
1213
1214         if(veh.head_model != "")
1215         {
1216                 setmodel(self.tur_head, veh.head_model);
1217                 setattachment(self.tur_head, self, veh.tag_head);
1218                 setattachment(self.vehicle_hudmodel, self.tur_head, veh.tag_hud);
1219                 setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view);
1220         }
1221         else
1222         {
1223                 setattachment(self.tur_head, self, "");
1224                 setattachment(self.vehicle_hudmodel, self, veh.tag_hud);
1225                 setattachment(self.vehicle_viewport, self.vehicle_hudmodel, veh.tag_view);
1226         }
1227
1228         setsize(self, veh.mins, veh.maxs);
1229
1230         if(!nodrop)
1231         {
1232                 setorigin(self, self.origin);
1233                 tracebox(self.origin + '0 0 100', veh.mins, veh.maxs, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
1234                 setorigin(self, trace_endpos);
1235         }
1236
1237         self.pos1 = self.origin;
1238         self.pos2 = self.angles;
1239         self.tur_head.team = self.team;
1240
1241         VEH_ACTION(vehicle_id, VR_SETUP);
1242
1243         if(self.active == ACTIVE_NOT)
1244                 self.nextthink = 0; // wait until activated
1245         else if(autocvar_g_vehicles_delayspawn)
1246                 self.nextthink = time + self.respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter);
1247         else
1248                 self.nextthink = time + game_starttime;
1249
1250         if(MUTATOR_CALLHOOK(VehicleSpawn))
1251                 return false;
1252
1253         return true;
1254 }