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