]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/vehicles/vehicles.qc
647c6c77eb9e3aac05262deadd3d7ce4fb21195b
[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
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(self.vehicle_health <= 0)
416     {
417         if(self.owner)
418             if(self.vehicle_flags & VHF_DEATHEJECT)
419                 vehicles_exit(VHEF_EJECT);
420             else
421                 vehicles_exit(VHEF_RELESE);
422
423         self.vehicle_die();
424     }
425 }
426
427 void vehicles_return()
428 {
429     pointparticles(particleeffectnum("teleport"), self.enemy.origin + '0 0 64', '0 0 0', 1);
430     self.enemy.think = self.use;
431     self.enemy.nextthink = time;
432     remove(self);
433 }
434
435 void vehicles_clearrturn()
436 {
437     entity ret;
438     // Remove "return helper", if any.
439     ret = findchain(classname, "vehicle_return");
440     while(ret)
441     {
442         if(ret.enemy == self)
443         {
444             ret.think = SUB_Remove;
445             ret.nextthink = time + 0.1;
446             return;
447         }
448         ret = ret.chain;
449     }
450 }
451
452 void vehicles_setreturn(float retime, void() respawn_proc)
453 {
454     vehicles_clearrturn();
455
456     if (self.deadflag == DEAD_NO)
457     {
458         entity ret;
459
460         ret = spawn();
461         ret.classname = "vehicle_return";
462         ret.enemy = self;
463         ret.think = vehicles_return;
464         ret.nextthink = time + retime;
465         ret.use = respawn_proc;
466     }
467 }
468
469 float vehicles_customizeentityforclient()
470 {
471     if(self.deadflag  == DEAD_DEAD)
472         return FALSE;
473     else
474         return TRUE;
475 }
476
477 void vehicles_configcheck(string  configname, float check_cvar)
478 {
479     if(check_cvar == 0)
480         localcmd(strcat("exec ", configname, "\n"));
481 }
482
483 void vehicles_reset_colors()
484 {
485     entity e;
486     float _effects, _colormap;
487     vector _glowmod, _colormod;
488
489     if(autocvar_g_nodepthtestplayers)
490         _effects = EF_NODEPTHTEST;
491
492     if(autocvar_g_fullbrightplayers)
493         _effects |= EF_FULLBRIGHT;
494
495     if(self.team)
496         _colormap = 1024 + (self.team - 1) * 17;
497     else
498         _colormap = 1024;
499
500     _glowmod    = '0 0 0';
501     _colormod   = '0 0 0';
502
503     // Find all ents attacked to main model and setup effects, colormod etc.
504     e = findchainentity(tag_entity, self);
505     while(e)
506     {
507         e.effects   = _effects;
508         e.colormod  = _colormod;
509         e.colormap  = _colormap;
510         e.alpha     = 1;
511
512         e = e.chain;
513     }
514
515     self.effects  = _effects;
516     self.colormod = _colormod;
517     self.colormap = _colormap;
518
519     self.alpha          = 1;
520     self.avelocity      = '0 0 0';
521     self.velocity       = '0 0 0';
522
523 }
524
525 float vehicle_initialize(string  net_name,
526                          string  bodymodel,
527                          string  topmodel,
528                          string  hudmodel,
529                          string  toptag,
530                          string  hudtag,
531                          string  viewtag,
532                          float   vhud,
533                          vector min_s,
534                          vector max_s,
535                          float  nodrop,
536                          void()  spawnproc,
537                          float() physproc,
538                          void()  enterproc,
539                          void(float extflag) exitfunc,
540                          void() dieproc,
541                          void() thinkproc )
542 {
543     addstat(STAT_HUD, AS_INT,  hud);
544         addstat(STAT_VEHICLESTAT_HEALTH,  AS_FLOAT, vehicle_health);
545         addstat(STAT_VEHICLESTAT_SHIELD,  AS_FLOAT, vehicle_shield);
546         addstat(STAT_VEHICLESTAT_ENERGY,  AS_FLOAT, vehicle_energy);
547
548         addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT,   vehicle_ammo1);
549         addstat(STAT_VEHICLESTAT_RELOAD1, AS_FLOAT, vehicle_reload1);
550
551         addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT,   vehicle_ammo2);
552         addstat(STAT_VEHICLESTAT_RELOAD2, AS_FLOAT, vehicle_reload2);
553
554
555     if(bodymodel == "")
556         error("vehicles: missing bodymodel!");
557
558     if(hudmodel == "")
559         error("vehicles: missing hudmodel!");
560
561     if(net_name == "")
562         self.netname = self.classname;
563     else
564         self.netname = net_name;
565
566     if(self.team && !teams_matter)
567         self.team = 0;
568
569     self.vehicle_flags |= VHF_ISVEHICLE;
570
571     setmodel(self, bodymodel);
572
573     self.vehicle_viewport   = spawn();
574     self.vehicle_hudmodel   = spawn();
575     self.tur_head           = spawn();
576     self.tur_head.owner     = self;
577     self.takedamage         = DAMAGE_AIM;
578     self.bot_attack         = TRUE;
579     self.iscreature         = TRUE;
580     self.hud                = vhud;
581
582     self.customizeentityforclient = vehicles_customizeentityforclient;
583     self.vehicle_die        = dieproc;
584     self.vehicle_exit       = exitfunc;
585     self.vehicle_enter      = enterproc;
586     self.PlayerPhysplug     = physproc;
587     self.event_damage       = vehicles_damage;
588     self.touch              = vehicles_touch;
589     self.think              = spawnproc;
590     self.nextthink          = time;
591
592     if(autocvar_g_nodepthtestplayers)
593         self.effects = self.effects | EF_NODEPTHTEST;
594
595     if(autocvar_g_fullbrightplayers)
596         self.effects = self.effects | EF_FULLBRIGHT;
597
598     setmodel(self.vehicle_hudmodel, hudmodel);
599     setmodel(self.vehicle_viewport, "null");
600
601     if(topmodel != "")
602     {
603         setmodel(self.tur_head, topmodel);
604         setattachment(self.tur_head, self, toptag);
605         setattachment(self.vehicle_hudmodel, self.tur_head, hudtag);
606         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
607     }
608     else
609     {
610         setattachment(self.tur_head, self, "");
611         setattachment(self.vehicle_hudmodel, self, hudtag);
612         setattachment(self.vehicle_viewport, self.vehicle_hudmodel, viewtag);
613     }
614
615     setsize(self, min_s, max_s);
616     if not (nodrop)
617     {
618         setorigin(self, self.origin);
619         tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
620         setorigin(self, trace_endpos);
621     }
622
623     self.pos1 = self.origin;
624     self.pos2 = self.angles;
625
626     return TRUE;
627 }
628
629
630 void bugmenot()
631 {
632     self.vehicle_exit       = self.vehicle_exit;
633     self.vehicle_enter      = self.vehicle_exit;
634     self.vehicle_die        = self.vehicle_exit;
635     self.vehicle_spawn      = self.vehicle_exit;
636     self.AuxiliaryXhair     = self.AuxiliaryXhair;
637 }