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