]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/impulse.qc
Optimize vehicle impact code by only calling vlen() if damage would be taken
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / impulse.qc
1 #include "impulse.qh"
2 #include "round_handler.qh"
3
4 #include "weapons/throwing.qh"
5 #include "command/common.qh"
6 #include "cheats.qh"
7 #include "clientkill.qh"
8 #include "weapons/selection.qh"
9 #include "weapons/tracing.qh"
10 #include "weapons/weaponsystem.qh"
11
12 #include <common/state.qh>
13
14 #include "../common/minigames/sv_minigames.qh"
15
16 #include <common/weapons/_all.qh>
17 #include "../common/vehicles/sv_vehicles.qh"
18
19 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
20
21 .entity vehicle;
22
23 #define IMPULSE(id) _IMPULSE(IMP_##id)
24 #define _IMPULSE(id) \
25         void id##_handle(entity this); \
26         STATIC_INIT_LATE(id) \
27         { \
28                 id.impulse_handle = id##_handle; \
29         } \
30         void id##_handle(entity this)
31
32 /**
33  * Impulse map:
34  *
35  * 0 reserved (no input)
36  *
37  * 99: loaded
38  *
39  * 140: moving clone
40  * 141: ctf speedrun
41  * 142: fixed clone
42  * 143: emergency teleport
43  * 148: unfairly eliminate
44  *
45  * TODO:
46  * 200 to 209: prev weapon shortcuts
47  * 210 to 219: best weapon shortcuts
48  * 220 to 229: next weapon shortcuts
49  * 230 to 253: individual weapons (up to 24)
50  */
51
52 // weapon switching impulses
53
54 #define X(i) \
55         IMPULSE(weapon_group_##i) \
56         { \
57                 if (IS_DEAD(this)) \
58                 { \
59                         this.impulse = IMP_weapon_group_##i.impulse; \
60                         return; \
61                 } \
62                 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \
63                 { \
64                         .entity weaponentity = weaponentities[slot]; \
65                         W_NextWeaponOnImpulse(this, i, weaponentity); \
66                         if(autocvar_g_weaponswitch_debug != 1) \
67                                 break; \
68                 } \
69         }
70 X(1)
71 X(2)
72 X(3)
73 X(4)
74 X(5)
75 X(6)
76 X(7)
77 X(8)
78 X(9)
79 X(0)
80 #undef X
81
82 // custom order weapon cycling
83
84 #define X(i, dir) \
85         IMPULSE(weapon_priority_##i##_##dir) \
86         { \
87                 if (this.vehicle) return; \
88                 if (IS_DEAD(this)) \
89                 { \
90                         this.impulse = IMP_weapon_priority_##i##_##dir.impulse; \
91                         return; \
92                 } \
93                 noref int prev = -1; \
94                 noref int best =  0; \
95                 noref int next = +1; \
96                 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \
97                 { \
98                         .entity weaponentity = weaponentities[slot]; \
99                         W_CycleWeapon(this, CS(this).cvar_cl_weaponpriorities[i], dir, weaponentity); \
100                         if(autocvar_g_weaponswitch_debug != 1) \
101                                 break; \
102                 } \
103         }
104 X(0, prev)
105 X(1, prev)
106 X(2, prev)
107 X(3, prev)
108 X(4, prev)
109 X(5, prev)
110 X(6, prev)
111 X(7, prev)
112 X(8, prev)
113 X(9, prev)
114
115 X(0, best)
116 X(1, best)
117 X(2, best)
118 X(3, best)
119 X(4, best)
120 X(5, best)
121 X(6, best)
122 X(7, best)
123 X(8, best)
124 X(9, best)
125
126 X(0, next)
127 X(1, next)
128 X(2, next)
129 X(3, next)
130 X(4, next)
131 X(5, next)
132 X(6, next)
133 X(7, next)
134 X(8, next)
135 X(9, next)
136 #undef X
137
138 // direct weapons
139
140 #define X(i) \
141         IMPULSE(weapon_byid_##i) \
142         { \
143                 if (this.vehicle) return; \
144                 if (IS_DEAD(this)) \
145                 { \
146                         this.impulse = IMP_weapon_byid_##i.impulse; \
147                         return; \
148                 } \
149                 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \
150                 { \
151                         .entity weaponentity = weaponentities[slot]; \
152                         W_SwitchWeapon_TryOthers(this, Weapons_from(WEP_FIRST + i), weaponentity); \
153                         if(autocvar_g_weaponswitch_debug != 1) \
154                                 break; \
155                 } \
156         }
157 X(0)
158 X(1)
159 X(2)
160 X(3)
161 X(4)
162 X(5)
163 X(6)
164 X(7)
165 X(8)
166 X(9)
167 X(10)
168 X(11)
169 X(12)
170 X(13)
171 X(14)
172 X(15)
173 X(16)
174 X(17)
175 X(18)
176 X(19)
177 X(20)
178 X(21)
179 X(22)
180 X(23)
181 #undef X
182
183 IMPULSE(weapon_next_byid)
184 {
185         if (this.vehicle) return;
186         if (IS_DEAD(this))
187         {
188                 this.impulse = IMP_weapon_next_byid.impulse;
189                 return;
190         }
191         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
192         {
193                 .entity weaponentity = weaponentities[slot];
194                 W_NextWeapon(this, 0, weaponentity);
195
196                 if(autocvar_g_weaponswitch_debug != 1)
197                         break;
198         }
199 }
200
201 IMPULSE(weapon_prev_byid)
202 {
203         if (this.vehicle) return;
204         if (IS_DEAD(this))
205         {
206                 this.impulse = IMP_weapon_prev_byid.impulse;
207                 return;
208         }
209         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
210         {
211                 .entity weaponentity = weaponentities[slot];
212                 W_PreviousWeapon(this, 0, weaponentity);
213
214                 if(autocvar_g_weaponswitch_debug != 1)
215                         break;
216         }
217 }
218
219 IMPULSE(weapon_next_bygroup)
220 {
221         if (this.vehicle) return;
222         if (IS_DEAD(this))
223         {
224                 this.impulse = IMP_weapon_next_bygroup.impulse;
225                 return;
226         }
227         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
228         {
229                 .entity weaponentity = weaponentities[slot];
230                 W_NextWeapon(this, 1, weaponentity);
231
232                 if(autocvar_g_weaponswitch_debug != 1)
233                         break;
234         }
235 }
236
237 IMPULSE(weapon_prev_bygroup)
238 {
239         if (this.vehicle) return;
240         if (IS_DEAD(this))
241         {
242                 this.impulse = IMP_weapon_prev_bygroup.impulse;
243                 return;
244         }
245         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
246         {
247                 .entity weaponentity = weaponentities[slot];
248                 W_PreviousWeapon(this, 1, weaponentity);
249
250                 if(autocvar_g_weaponswitch_debug != 1)
251                         break;
252         }
253 }
254
255 IMPULSE(weapon_next_bypriority)
256 {
257         if (this.vehicle) return;
258         if (IS_DEAD(this))
259         {
260                 this.impulse = IMP_weapon_next_bypriority.impulse;
261                 return;
262         }
263         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
264         {
265                 .entity weaponentity = weaponentities[slot];
266                 W_NextWeapon(this, 2, weaponentity);
267
268                 if(autocvar_g_weaponswitch_debug != 1)
269                         break;
270         }
271 }
272
273 IMPULSE(weapon_prev_bypriority)
274 {
275         if (this.vehicle) return;
276         if (IS_DEAD(this))
277         {
278                 this.impulse = IMP_weapon_prev_bypriority.impulse;
279                 return;
280         }
281         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
282         {
283                 .entity weaponentity = weaponentities[slot];
284                 W_PreviousWeapon(this, 2, weaponentity);
285
286                 if(autocvar_g_weaponswitch_debug != 1)
287                         break;
288         }
289 }
290
291 IMPULSE(weapon_last)
292 {
293         if (this.vehicle) return;
294         if (IS_DEAD(this)) return;
295         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
296         {
297                 .entity weaponentity = weaponentities[slot];
298                 W_LastWeapon(this, weaponentity);
299
300                 if(autocvar_g_weaponswitch_debug != 1)
301                         break;
302         }
303 }
304
305 IMPULSE(weapon_best)
306 {
307         if (this.vehicle) return;
308         if (IS_DEAD(this)) return;
309         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
310         {
311                 .entity weaponentity = weaponentities[slot];
312                 W_SwitchWeapon(this, w_getbestweapon(this, weaponentity), weaponentity);
313
314                 if(autocvar_g_weaponswitch_debug != 1)
315                         break;
316         }
317 }
318
319 IMPULSE(weapon_drop)
320 {
321         if (this.vehicle) return;
322         if (IS_DEAD(this)) return;
323         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
324         {
325                 .entity weaponentity = weaponentities[slot];
326                 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
327
328                 if(autocvar_g_weaponswitch_debug != 1)
329                         break;
330         }
331 }
332
333 IMPULSE(weapon_reload)
334 {
335         if (this.vehicle) return;
336         if (IS_DEAD(this)) return;
337         if (weaponLocked(this)) return;
338         entity actor = this;
339         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
340         {
341                 .entity weaponentity = weaponentities[slot];
342                 Weapon w = this.(weaponentity).m_weapon;
343                 w.wr_reload(w, actor, weaponentity);
344
345                 // allow reloading all active slots?
346                 //if(autocvar_g_weaponswitch_debug != 1)
347                         //break;
348         }
349 }
350
351 void ImpulseCommands(entity this)
352 {
353         if (game_stopped) return;
354
355         int imp = CS(this).impulse;
356         if (!imp) return;
357         CS(this).impulse = 0;
358
359         if (MinigameImpulse(this, imp)) return;
360
361         if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
362
363         // allow only weapon change impulses when not in round time
364         if (round_handler_IsActive() && !round_handler_IsRoundStarted())
365         {
366                 #define X(id) case IMP_##id.impulse:
367                 switch (imp)
368                 {
369                         X(weapon_group_0)
370                         X(weapon_group_1)
371                         X(weapon_group_2)
372                         X(weapon_group_3)
373                         X(weapon_group_4)
374                         X(weapon_group_5)
375                         X(weapon_group_6)
376                         X(weapon_group_7)
377                         X(weapon_group_8)
378                         X(weapon_group_9)
379                         X(weapon_next_byid)
380                         X(weapon_prev_byid)
381                         X(weapon_next_bygroup)
382                         X(weapon_prev_bygroup)
383                         X(weapon_next_bypriority)
384                         X(weapon_prev_bypriority)
385                         X(weapon_last)
386                         X(weapon_best)
387                         X(weapon_reload)
388                         X(weapon_priority_0_prev)
389             X(weapon_priority_1_prev)
390             X(weapon_priority_2_prev)
391             X(weapon_priority_3_prev)
392             X(weapon_priority_4_prev)
393             X(weapon_priority_5_prev)
394             X(weapon_priority_6_prev)
395             X(weapon_priority_7_prev)
396             X(weapon_priority_8_prev)
397             X(weapon_priority_9_prev)
398             X(weapon_priority_0_next)
399                         X(weapon_priority_1_next)
400                         X(weapon_priority_2_next)
401                         X(weapon_priority_3_next)
402                         X(weapon_priority_4_next)
403                         X(weapon_priority_5_next)
404                         X(weapon_priority_6_next)
405                         X(weapon_priority_7_next)
406                         X(weapon_priority_8_next)
407                         X(weapon_priority_9_next)
408                         X(weapon_priority_0_best)
409             X(weapon_priority_1_best)
410             X(weapon_priority_2_best)
411             X(weapon_priority_3_best)
412             X(weapon_priority_4_best)
413             X(weapon_priority_5_best)
414             X(weapon_priority_6_best)
415             X(weapon_priority_7_best)
416             X(weapon_priority_8_best)
417             X(weapon_priority_9_best)
418             X(weapon_byid_0)
419             X(weapon_byid_1)
420             X(weapon_byid_2)
421             X(weapon_byid_3)
422             X(weapon_byid_4)
423             X(weapon_byid_5)
424             X(weapon_byid_6)
425             X(weapon_byid_7)
426             X(weapon_byid_8)
427             X(weapon_byid_9)
428             X(weapon_byid_10)
429             X(weapon_byid_11)
430             X(weapon_byid_12)
431             X(weapon_byid_13)
432             X(weapon_byid_14)
433             X(weapon_byid_15)
434             X(weapon_byid_16)
435             X(weapon_byid_17)
436             X(weapon_byid_18)
437             X(weapon_byid_19)
438             X(weapon_byid_20)
439             X(weapon_byid_21)
440             X(weapon_byid_22)
441             X(weapon_byid_23)
442                         break;
443                         default: return;
444                 }
445 #undef X
446         }
447
448         if (vehicle_impulse(this, imp)) return;
449
450         if (CheatImpulse(this, imp)) return;
451
452         FOREACH(IMPULSES, it.impulse == imp, {
453                 void(entity) f = it.impulse_handle;
454                 if (!f) continue;
455                 f(this);
456                 return;
457         });
458 }
459
460 IMPULSE(use)
461 {
462         PlayerUseKey(this);
463 }
464
465 IMPULSE(waypoint_personal_here)
466 {
467         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
468         if (wp) WaypointSprite_Ping(wp);
469         sprint(this, "personal waypoint spawned at location\n");
470 }
471
472 IMPULSE(waypoint_personal_crosshair)
473 {
474         WarpZone_crosshair_trace(this);
475         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
476         if (wp) WaypointSprite_Ping(wp);
477         sprint(this, "personal waypoint spawned at crosshair\n");
478 }
479
480 IMPULSE(waypoint_personal_death)
481 {
482         if (!this.death_origin) return;
483         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
484         if (wp) WaypointSprite_Ping(wp);
485         sprint(this, "personal waypoint spawned at death location\n");
486 }
487
488 IMPULSE(waypoint_here_follow)
489 {
490         if (!teamplay) return;
491         if (IS_DEAD(this)) return;
492         if (!MUTATOR_CALLHOOK(HelpMePing, this))
493         {
494                 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
495                 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
496                 else WaypointSprite_Ping(wp);
497         }
498         sprint(this, "HELP ME attached\n");
499 }
500
501 IMPULSE(waypoint_here_here)
502 {
503         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
504         if (wp) WaypointSprite_Ping(wp);
505         sprint(this, "HERE spawned at location\n");
506 }
507
508 IMPULSE(waypoint_here_crosshair)
509 {
510         WarpZone_crosshair_trace_plusvisibletriggers(this);
511         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
512         if (wp) WaypointSprite_Ping(wp);
513         sprint(this, "HERE spawned at crosshair\n");
514 }
515
516 IMPULSE(waypoint_here_death)
517 {
518         if (!this.death_origin) return;
519         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
520         if (wp) WaypointSprite_Ping(wp);
521         sprint(this, "HERE spawned at death location\n");
522 }
523
524 IMPULSE(waypoint_danger_here)
525 {
526         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
527         if (wp) WaypointSprite_Ping(wp);
528         sprint(this, "DANGER spawned at location\n");
529 }
530
531 IMPULSE(waypoint_danger_crosshair)
532 {
533         WarpZone_crosshair_trace(this);
534         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
535         if (wp) WaypointSprite_Ping(wp);
536         sprint(this, "DANGER spawned at crosshair\n");
537 }
538
539 IMPULSE(waypoint_danger_death)
540 {
541         if (!this.death_origin) return;
542         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
543         if (wp) WaypointSprite_Ping(wp);
544         sprint(this, "DANGER spawned at death location\n");
545 }
546
547 IMPULSE(waypoint_clear_personal)
548 {
549         WaypointSprite_ClearPersonal(this);
550         if (this.personal)
551         {
552                 delete(this.personal);
553                 this.personal = NULL;
554
555                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
556                         ClientKill(this);
557         }
558         sprint(this, "personal waypoint cleared\n");
559 }
560
561 IMPULSE(waypoint_clear)
562 {
563         WaypointSprite_ClearOwned(this);
564         if (this.personal)
565         {
566                 delete(this.personal);
567                 this.personal = NULL;
568                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
569                         ClientKill(this);
570         }
571         sprint(this, "all waypoints cleared\n");
572 }