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