]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/vehicles/vehicles.qc
Experiment: Set MOVETYPE_TOSS on the vehicle when unmanned and taking damage
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / vehicles / vehicles.qc
1 float autocvar_g_vehicles_crush_dmg;
2 float autocvar_g_vehicles_crush_force;
3
4 void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
5 void vehicles_return();
6 void vehicles_clearrturn();
7
8 #define MAX_AXH 4
9 .entity AuxiliaryXhair[MAX_AXH];
10
11 float SendAuxiliaryXhair(entity to, float sf)
12 {
13
14         WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR);
15
16         WriteByte(MSG_ENTITY, self.cnt);
17
18         WriteCoord(MSG_ENTITY, self.origin_x);
19         WriteCoord(MSG_ENTITY, self.origin_y);
20         WriteCoord(MSG_ENTITY, self.origin_z);
21
22     WriteByte(MSG_ENTITY, rint(self.colormod_x * 255));
23     WriteByte(MSG_ENTITY, rint(self.colormod_y * 255));
24     WriteByte(MSG_ENTITY, rint(self.colormod_z * 255));
25
26     return TRUE;
27 }
28
29 void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id)
30 {
31     entity axh;
32
33     axh_id = bound(0, axh_id, MAX_AXH);
34     axh = own.AuxiliaryXhair[axh_id];
35
36     if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
37     {
38         axh                     = spawn();
39         axh.cnt                 = axh_id;
40         axh.drawonlytoclient    = own;
41         axh.owner               = own;
42         Net_LinkEntity(axh, FALSE, 0, SendAuxiliaryXhair);
43     }
44
45     setorigin(axh, loc);
46     axh.colormod            = clr;
47     axh.SendFlags           = 0x01;
48     own.AuxiliaryXhair[axh_id] = axh;
49 }
50
51 /*
52 // SVC_TEMPENTITY based, horrible with even 50 ping. hm.
53 // WriteByte(MSG_ONE, SVC_TEMPENTITY) uses reliable messagess, never use for thinsg that need continous updates.
54 void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id)
55 {
56         msg_entity = own;
57
58         WriteByte(MSG_ONE, SVC_TEMPENTITY);
59         WriteByte(MSG_ONE, TE_CSQC_AUXILIARYXHAIR);
60
61         WriteByte(MSG_ONE, axh_id);
62
63         WriteCoord(MSG_ONE, loc_x);
64         WriteCoord(MSG_ONE, loc_y);
65         WriteCoord(MSG_ONE, loc_z);
66
67     WriteByte(MSG_ONE, rint(clr_x * 255));
68     WriteByte(MSG_ONE, rint(clr_y * 255));
69     WriteByte(MSG_ONE, rint(clr_z * 255));
70
71 }
72 */
73
74 void CSQCVehicleSetup(entity own, float vehicle_id)
75 {
76         msg_entity = own;
77
78         WriteByte(MSG_ONE, SVC_TEMPENTITY);
79         WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP);
80         WriteByte(MSG_ONE, vehicle_id);
81 }
82
83 .entity lock_target;
84 .float  lock_strength;
85 .float  lock_time;
86 void vehicles_locktarget2(float incr, float decr, float _lock_time)
87 {
88     if(self.lock_target && self.lock_target.deadflag != DEAD_NO)
89         self.lock_target = world;
90
91     if(trace_ent != world)
92     {
93         if(teams_matter && trace_ent.team == self.team)
94             trace_ent = world;
95
96         if(trace_ent.deadflag != DEAD_NO)
97             trace_ent = world;
98
99         if not (trace_ent.vehicle_flags & VHF_ISVEHICLE || trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
100             trace_ent = world;
101     }
102
103     if(self.lock_time > time)
104         return;
105
106     /*if not (trace_ent.vehicle_flags & VHF_ISVEHICLE || trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
107         self.lock_strength = min(self.lock_strength - decr, 1);
108     else if(trace_ent.deadflag != DEAD_NO)
109         self.lock_strength = min(self.lock_strength - decr, 1);*/
110
111     if(self.lock_target == world && trace_ent != world)
112         self.lock_target = trace_ent;
113
114     // Have a locking target
115     // Trace hit current target
116     if(trace_ent == self.lock_target && trace_ent != world)
117     {
118         self.lock_strength = min(self.lock_strength + incr, 1);
119         if(self.lock_strength == 1)
120             self.lock_time = time + _lock_time;
121     }
122     else
123     {
124         self.lock_strength = max(self.lock_strength - decr, 0);
125
126         if(self.lock_strength == 0)
127             self.lock_target = world;
128     }
129 }
130
131
132 #define VEHICLE_UPDATE_PLAYER(fld,vhname) \
133 self.owner.vehicle_##fld = self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld
134
135 #define vehicles_sweap_collision(orig,vel,dt,acm,mult) \
136 traceline(orig, orig + vel * dt, MOVE_NORMAL, self); \
137 if(trace_fraction != 1) \
138     acm += normalize(self.origin - trace_endpos) * (vlen(vel) * mult)
139
140 float  force_fromtag_power;
141 float  force_fromtag_normpower;
142 vector force_fromtag_origin;
143 vector vehicles_force_fromtag_hover(string tag_name, float spring_length, float max_power)
144 {
145     force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
146     v_forward  = normalize(v_forward) * -1;
147     traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
148
149     force_fromtag_power = (1 - trace_fraction) * max_power;
150     force_fromtag_normpower = force_fromtag_power / max_power;
151
152     return v_forward  * force_fromtag_power;
153 }
154
155 vector vehicles_force_fromtag_maglev(string tag_name, float spring_length, float max_power)
156 {
157
158     force_fromtag_origin = gettaginfo(self, gettagindex(self, tag_name));
159     v_forward  = normalize(v_forward) * -1;
160     traceline(force_fromtag_origin, force_fromtag_origin - (v_forward  * spring_length), MOVE_NORMAL, self);
161
162     if(trace_fraction == 1.0)
163     {
164         force_fromtag_normpower = -0.25;
165         return '0 0 -200';
166     }
167
168     force_fromtag_power = ((1 - trace_fraction) - trace_fraction) * max_power;
169     force_fromtag_normpower = force_fromtag_power / max_power;
170
171     return v_forward  * force_fromtag_power;
172 }
173
174 // Better way of determening whats crushable needed! (fl_crushable?)
175 float vehicles_crushable(entity e)
176 {
177     if(e.classname == "corpse")
178         return TRUE;
179
180     if(e.classname == "player")
181         return TRUE;
182
183     if(e.classname == "monster_zombie")
184         return TRUE;
185
186     return FALSE;
187 }
188
189 void vehicles_enter();
190 void vehicles_touch()
191 {
192     // Vehicle currently in use
193     if(self.owner)
194     {
195         // Colided with world?
196         if(other == world)
197         {
198             // Apply velocity based self damage here
199         }
200         else
201         {
202             if(other.vehicle_flags & VHF_ISVEHICLE)
203             {
204                 //other.velocity += self.velocity * (self.mass / other.mass);
205             }
206             else if(vehicles_crushable(other))
207             {
208                 if(vlen(self.velocity) != 0)
209                     Damage(other, self, self.owner, autocvar_g_vehicles_crush_dmg, DEATH_SBCRUSH, '0 0 0', normalize(other.origin - self.origin) * autocvar_g_vehicles_crush_force);
210             }
211         }
212
213         return;
214     }
215
216     if(other.classname != "player")
217         return;
218
219     if(other.deadflag != DEAD_NO)
220         return;
221
222     if(other.vehicle != world)
223         return;
224
225     // Remove this when bots know how to use vehicles.
226     if (clienttype(other) != CLIENTTYPE_REAL)
227         return;
228
229     vehicles_enter();
230 }
231
232 void vehicles_enter()
233 {
234    // Remove this when bots know how to use vehicles
235     if (clienttype(other) != CLIENTTYPE_REAL)
236         return;
237
238     if(self.phase > time)
239         return;
240
241     if(teams_matter)
242     if(self.team)
243     if(self.team != other.team)
244         return;
245
246     self.owner          = other;
247     self.switchweapon   = other.switchweapon;
248
249     self.vehicle_hudmodel.viewmodelforclient = self.owner;
250     self.event_damage         = vehicles_damage;
251     self.nextthink            = 0;
252     self.owner.angles         = self.angles;
253     self.owner.takedamage     = DAMAGE_NO;
254     self.owner.solid          = SOLID_NOT;
255     self.owner.movetype       = MOVETYPE_NOCLIP;
256     self.owner.alpha          = -1;
257     self.owner.vehicle        = self;
258     self.owner.event_damage   = SUB_Null;
259     self.owner.view_ofs       = '0 0 0';
260     self.colormap             = self.owner.colormap;
261     if(self.tur_head)
262         self.tur_head.colormap    = self.owner.colormap;
263
264     self.owner.hud            = self.hud;
265     self.owner.PlayerPhysplug = self.PlayerPhysplug;
266
267     self.owner.vehicle_ammo1    = self.vehicle_ammo1;
268     self.owner.vehicle_ammo2    = self.vehicle_ammo2;
269     self.owner.vehicle_reload1  = self.vehicle_reload1;
270     self.owner.vehicle_reload2  = self.vehicle_reload2;
271
272     other.flags &~= FL_ONGROUND;
273     self.flags  &~= FL_ONGROUND;
274
275     self.team                 = self.owner.team;
276     self.flags               -= FL_NOTARGET;
277
278     msg_entity = other;
279     WriteByte (MSG_ONE, SVC_SETVIEWPORT);
280     WriteEntity(MSG_ONE, self.vehicle_viewport);
281
282     WriteByte (MSG_ONE, SVC_SETVIEWANGLES);  // 10 = SVC_SETVIEWANGLES
283     if(self.tur_head)
284     {
285         WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x);    // tilt
286         WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y);    // yaw
287         WriteAngle(MSG_ONE, 0);    // roll
288     }
289     else
290     {
291         WriteByte (MSG_ONE, SVC_SETVIEWANGLES); // 10 = SVC_SETVIEWANGLES
292         WriteAngle(MSG_ONE,  self.angles_x * -1);    // tilt
293         WriteAngle(MSG_ONE,  self.angles_y);    // yaw
294         WriteAngle(MSG_ONE,  0);                // roll
295     }
296
297     vehicles_clearrturn();
298
299     CSQCVehicleSetup(self.owner, self.hud);
300
301     if(self.vehicle_enter)
302         self.vehicle_enter();
303 }
304
305 void vehicles_exit(float eject)
306 {
307         self.flags |= FL_NOTARGET;
308
309     if (self.owner)
310     {
311         msg_entity = self.owner;
312         WriteByte (MSG_ONE, SVC_SETVIEWPORT);
313         WriteEntity( MSG_ONE, self.owner);
314
315         WriteByte (MSG_ONE, SVC_SETVIEWANGLES); // 10 = SVC_SETVIEWANGLES
316         WriteAngle(MSG_ONE, 0);                 // tilt
317         WriteAngle(MSG_ONE, self.angles_y);     // yaw
318         WriteAngle(MSG_ONE, 0);                 // roll
319
320         setsize(self.owner, PL_MIN,PL_MAX);
321
322         self.owner.takedamage     = DAMAGE_AIM;
323         self.owner.solid          = SOLID_SLIDEBOX;
324         self.owner.movetype       = MOVETYPE_WALK;
325         self.owner.effects        &~= EF_NODRAW;
326         self.owner.alpha          = 1;
327         self.owner.PlayerPhysplug = SUB_Null;
328         self.owner.vehicle        = world;
329         self.owner.view_ofs       = PL_VIEW_OFS;
330         self.owner.event_damage   = PlayerDamage;
331         self.owner.hud            = HUD_NORMAL;
332         self.owner.switchweapon   = self.switchweapon;
333     }
334
335     self.vehicle_hudmodel.viewmodelforclient = self;
336         self.tur_head.nodrawtoclient             = self;
337
338     self.phase = time + 1;
339
340     if(!teams_matter)
341         self.team = 0;
342
343     if(self.vehicle_exit)
344         self.vehicle_exit(eject);
345
346     self.owner = world;
347 }
348
349
350 void vehicles_regen(.float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time)
351 {
352     if(self.regen_field < field_max)
353     if(self.timer + rpause < time)
354     {
355         self.regen_field = min(self.regen_field + regen * delta_time, field_max);
356
357         if(self.owner)
358             self.owner.regen_field = self.regen_field / field_max;
359     }
360 }
361
362 void shieldhit_think()
363 {
364     self.alpha -= 0.1;
365     if (self.alpha <= 0)
366     {
367         setmodel(self, "");
368         self.alpha = -1;
369     }
370     else
371     {
372         self.nextthink = time + 0.1;
373     }
374 }
375
376 void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
377 {
378     float ddmg_take;
379
380     self.dmg_time = time;
381
382     if((self.vehicle_flags & VHF_HASSHIELD) && (self.vehicle_shield > 0))
383     {
384         if (wasfreed(self.tur_head.enemy) || self.tur_head.enemy == world)
385         {
386             self.tur_head.enemy = spawn();
387             self.tur_head.enemy.effects = EF_LOWPRECISION;
388         }
389
390         setmodel(self.tur_head.enemy, "models/vhshield.md3");
391         setattachment(self.tur_head.enemy, self, "");
392
393         self.tur_head.enemy.colormod    = '1 1 1';
394         self.tur_head.enemy.alpha       = 0.45;
395         self.tur_head.enemy.scale       = 256 / vlen(self.maxs - self.mins);
396         self.tur_head.enemy.angles      = vectoangles(normalize(hitloc - self.origin)) - self.angles;
397         self.tur_head.enemy.think       = shieldhit_think;
398         self.tur_head.enemy.nextthink   = time;
399
400         self.vehicle_shield -= damage;
401         if(self.vehicle_shield < 0)
402         {
403             self.tur_head.enemy.colormod = '2 0 0';
404             ddmg_take = fabs(self.vehicle_shield);
405             self.vehicle_shield         = 0;
406             self.tur_head.enemy.alpha   = 0.75;
407             self.vehicle_health         -= ddmg_take;
408         }
409     }
410     else
411         self.vehicle_health -= damage;
412
413     self.velocity += force * (vlen(force) / self.mass);
414
415     if not(self.owner)
416     {
417                 self.movetype &~= MOVETYPE_TOSS;
418     }
419
420     if(self.vehicle_health <= 0)
421     {
422         if(self.owner)
423             if(self.vehicle_flags & VHF_DEATHEJECT)
424                 vehicles_exit(VHEF_EJECT);
425             else
426                 vehicles_exit(VHEF_RELESE);
427
428         self.vehicle_die();
429     }
430 }
431
432 void vehicles_return()
433 {
434     pointparticles(particleeffectnum("teleport"), self.enemy.origin + '0 0 64', '0 0 0', 1);
435     self.enemy.think = self.use;
436     self.enemy.nextthink = time;
437     remove(self);
438 }
439
440 void vehicles_clearrturn()
441 {
442     entity ret;
443     // Remove "return helper", if any.
444     ret = findchain(classname, "vehicle_return");
445     while(ret)
446     {
447         if(ret.enemy == self)
448         {
449             ret.think = SUB_Remove;
450             ret.nextthink = time + 0.1;
451             return;
452         }
453         ret = ret.chain;
454     }
455 }
456
457 void vehicles_setreturn(float retime, void() respawn_proc)
458 {
459     vehicles_clearrturn();
460
461     if (self.deadflag == DEAD_NO)
462     {
463         entity ret;
464
465         ret = spawn();
466         ret.classname = "vehicle_return";
467         ret.enemy = self;
468         ret.think = vehicles_return;
469         ret.nextthink = time + retime;
470         ret.use = respawn_proc;
471     }
472 }
473
474 float vehicles_customizeentityforclient()
475 {
476     if(self.deadflag  == DEAD_DEAD)
477         return FALSE;
478     else
479         return TRUE;
480 }
481
482 void vehicles_configcheck(string  configname, float check_cvar)
483 {
484     if(check_cvar == 0)
485         localcmd(strcat("exec ", configname, "\n"));
486 }
487
488 void vehicles_reset_colors()
489 {
490     entity e;
491     float _effects, _colormap;
492     vector _glowmod, _colormod;
493
494     if(autocvar_g_nodepthtestplayers)
495         _effects = EF_NODEPTHTEST;
496
497     if(autocvar_g_fullbrightplayers)
498         _effects |= EF_FULLBRIGHT;
499
500     if(self.team)
501         _colormap = 1024 + (self.team - 1) * 17;
502     else
503         _colormap = 1024;
504
505     _glowmod    = '0 0 0';
506     _colormod   = '0 0 0';
507
508     // Find all ents attacked to main model and setup effects, colormod etc.
509     e = findchainentity(tag_entity, self);
510     while(e)
511     {
512         e.effects   = _effects;
513         e.colormod  = _colormod;
514         e.colormap  = _colormap;
515         e.alpha     = 1;
516
517         e = e.chain;
518     }
519
520     self.effects  = _effects;
521     self.colormod = _colormod;
522     self.colormap = _colormap;
523
524     self.alpha          = 1;
525     self.avelocity      = '0 0 0';
526     self.velocity       = '0 0 0';
527
528 }
529
530 float vehicle_initialize(string  net_name,
531                          string  bodymodel,
532                          string  topmodel,
533                          string  hudmodel,
534                          string  toptag,
535                          string  hudtag,
536                          string  viewtag,
537                          float   vhud,
538                          vector min_s,
539                          vector max_s,
540                          float  nodrop,
541                          void()  spawnproc,
542                          float() physproc,
543                          void()  enterproc,
544                          void(float extflag) exitfunc,
545                          void() dieproc,
546                          void() thinkproc )
547 {
548     addstat(STAT_HUD, AS_INT,  hud);
549         addstat(STAT_VEHICLESTAT_HEALTH,  AS_FLOAT, vehicle_health);
550         addstat(STAT_VEHICLESTAT_SHIELD,  AS_FLOAT, vehicle_shield);
551         addstat(STAT_VEHICLESTAT_ENERGY,  AS_FLOAT, vehicle_energy);
552
553         addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT,   vehicle_ammo1);
554         addstat(STAT_VEHICLESTAT_RELOAD1, AS_FLOAT, vehicle_reload1);
555
556         addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT,   vehicle_ammo2);
557         addstat(STAT_VEHICLESTAT_RELOAD2, AS_FLOAT, vehicle_reload2);
558
559
560     if(bodymodel == "")
561         error("vehicles: missing bodymodel!");
562
563     if(hudmodel == "")
564         error("vehicles: missing hudmodel!");
565
566     if(net_name == "")
567         self.netname = self.classname;
568     else
569         self.netname = net_name;
570
571     if(self.team && !teams_matter)
572         self.team = 0;
573
574     self.vehicle_flags |= VHF_ISVEHICLE;
575
576     setmodel(self, bodymodel);
577
578     self.vehicle_viewport   = spawn();
579     self.vehicle_hudmodel   = spawn();
580     self.tur_head           = spawn();
581     self.tur_head.owner     = self;
582     self.takedamage         = DAMAGE_AIM;
583     self.bot_attack         = TRUE;
584     self.iscreature         = TRUE;
585     self.hud                = vhud;
586
587     self.customizeentityforclient = vehicles_customizeentityforclient;
588     self.vehicle_die        = dieproc;
589     self.vehicle_exit       = exitfunc;
590     self.vehicle_enter      = enterproc;
591     self.PlayerPhysplug     = physproc;
592     self.event_damage       = vehicles_damage;
593     self.touch              = vehicles_touch;
594     self.think              = spawnproc;
595     self.nextthink          = time;
596
597     if(autocvar_g_nodepthtestplayers)
598         self.effects = self.effects | EF_NODEPTHTEST;
599
600     if(autocvar_g_fullbrightplayers)
601         self.effects = self.effects | EF_FULLBRIGHT;
602
603     setmodel(self.vehicle_hudmodel, hudmodel);
604     setmodel(self.vehicle_viewport, "null");
605
606     if(topmodel != "")
607     {
608         setmodel(self.tur_head, topmodel);
609         setattachment(self.tur_head, self, toptag);
610         setattachment(self.vehicle_hudmodel, self.tur_head, hudtag);
611         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
612     }
613     else
614     {
615         setattachment(self.tur_head, self, "");
616         setattachment(self.vehicle_hudmodel, self, hudtag);
617         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
618     }
619
620     setsize(self, min_s, max_s);
621     if not (nodrop)
622     {
623         setorigin(self, self.origin);
624         tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
625         setorigin(self, trace_endpos);
626     }
627
628     self.pos1 = self.origin;
629     self.pos2 = self.angles;
630
631     return TRUE;
632 }
633
634
635 void bugmenot()
636 {
637     self.vehicle_exit       = self.vehicle_exit;
638     self.vehicle_enter      = self.vehicle_exit;
639     self.vehicle_die        = self.vehicle_exit;
640     self.vehicle_spawn      = self.vehicle_exit;
641     self.AuxiliaryXhair     = self.AuxiliaryXhair;
642 }