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