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