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