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