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