Merge branch 'master' into terencehill/bot_waypoints
[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                 if(slot == 0 && autocvar_g_weaponswitch_debug != 1)
347                         break;
348         }
349 }
350
351 void ImpulseCommands(entity this)
352 {
353         if (game_stopped) return;
354
355         int imp = CS(this).impulse;
356         if (!imp) return;
357         CS(this).impulse = 0;
358
359         if (MinigameImpulse(this, imp)) return;
360
361         if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
362
363         // allow only weapon change impulses when not in round time
364         if (round_handler_IsActive() && !round_handler_IsRoundStarted())
365         {
366                 #define X(id) case IMP_##id.impulse:
367                 switch (imp)
368                 {
369                         X(weapon_group_0)
370                         X(weapon_group_1)
371                         X(weapon_group_2)
372                         X(weapon_group_3)
373                         X(weapon_group_4)
374                         X(weapon_group_5)
375                         X(weapon_group_6)
376                         X(weapon_group_7)
377                         X(weapon_group_8)
378                         X(weapon_group_9)
379                         X(weapon_next_byid)
380                         X(weapon_prev_byid)
381                         X(weapon_next_bygroup)
382                         X(weapon_prev_bygroup)
383                         X(weapon_next_bypriority)
384                         X(weapon_prev_bypriority)
385                         X(weapon_last)
386                         X(weapon_best)
387                         X(weapon_reload)
388                         X(weapon_priority_0_prev)
389             X(weapon_priority_1_prev)
390             X(weapon_priority_2_prev)
391             X(weapon_priority_3_prev)
392             X(weapon_priority_4_prev)
393             X(weapon_priority_5_prev)
394             X(weapon_priority_6_prev)
395             X(weapon_priority_7_prev)
396             X(weapon_priority_8_prev)
397             X(weapon_priority_9_prev)
398             X(weapon_priority_0_next)
399                         X(weapon_priority_1_next)
400                         X(weapon_priority_2_next)
401                         X(weapon_priority_3_next)
402                         X(weapon_priority_4_next)
403                         X(weapon_priority_5_next)
404                         X(weapon_priority_6_next)
405                         X(weapon_priority_7_next)
406                         X(weapon_priority_8_next)
407                         X(weapon_priority_9_next)
408                         X(weapon_priority_0_best)
409             X(weapon_priority_1_best)
410             X(weapon_priority_2_best)
411             X(weapon_priority_3_best)
412             X(weapon_priority_4_best)
413             X(weapon_priority_5_best)
414             X(weapon_priority_6_best)
415             X(weapon_priority_7_best)
416             X(weapon_priority_8_best)
417             X(weapon_priority_9_best)
418             X(weapon_byid_0)
419             X(weapon_byid_1)
420             X(weapon_byid_2)
421             X(weapon_byid_3)
422             X(weapon_byid_4)
423             X(weapon_byid_5)
424             X(weapon_byid_6)
425             X(weapon_byid_7)
426             X(weapon_byid_8)
427             X(weapon_byid_9)
428             X(weapon_byid_10)
429             X(weapon_byid_11)
430             X(weapon_byid_12)
431             X(weapon_byid_13)
432             X(weapon_byid_14)
433             X(weapon_byid_15)
434             X(weapon_byid_16)
435             X(weapon_byid_17)
436             X(weapon_byid_18)
437             X(weapon_byid_19)
438             X(weapon_byid_20)
439             X(weapon_byid_21)
440             X(weapon_byid_22)
441             X(weapon_byid_23)
442                         break;
443                         default: return;
444                 }
445 #undef X
446         }
447
448         if (vehicle_impulse(this, imp)) return;
449
450         if (CheatImpulse(this, imp)) return;
451
452         FOREACH(IMPULSES, it.impulse == imp, {
453                 void(entity) f = it.impulse_handle;
454                 if (!f) continue;
455                 f(this);
456                 return;
457         });
458 }
459
460 IMPULSE(use)
461 {
462         PlayerUseKey(this);
463 }
464
465 IMPULSE(waypoint_personal_here)
466 {
467         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
468         if (wp) WaypointSprite_Ping(wp);
469         sprint(this, "personal waypoint spawned at location\n");
470 }
471
472 IMPULSE(waypoint_personal_crosshair)
473 {
474         WarpZone_crosshair_trace(this);
475         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
476         if (wp) WaypointSprite_Ping(wp);
477         sprint(this, "personal waypoint spawned at crosshair\n");
478 }
479
480 IMPULSE(waypoint_personal_death)
481 {
482         if (!this.death_origin) return;
483         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
484         if (wp) WaypointSprite_Ping(wp);
485         sprint(this, "personal waypoint spawned at death location\n");
486 }
487
488 IMPULSE(waypoint_here_follow)
489 {
490         if (!teamplay) return;
491         if (IS_DEAD(this)) return;
492         if (!MUTATOR_CALLHOOK(HelpMePing, this))
493         {
494                 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
495                 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
496                 else WaypointSprite_Ping(wp);
497         }
498         sprint(this, "HELP ME attached\n");
499 }
500
501 IMPULSE(waypoint_here_here)
502 {
503         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
504         if (wp) WaypointSprite_Ping(wp);
505         sprint(this, "HERE spawned at location\n");
506 }
507
508 IMPULSE(waypoint_here_crosshair)
509 {
510         WarpZone_crosshair_trace(this);
511         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
512         if (wp) WaypointSprite_Ping(wp);
513         sprint(this, "HERE spawned at crosshair\n");
514 }
515
516 IMPULSE(waypoint_here_death)
517 {
518         if (!this.death_origin) return;
519         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
520         if (wp) WaypointSprite_Ping(wp);
521         sprint(this, "HERE spawned at death location\n");
522 }
523
524 IMPULSE(waypoint_danger_here)
525 {
526         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
527         if (wp) WaypointSprite_Ping(wp);
528         sprint(this, "DANGER spawned at location\n");
529 }
530
531 IMPULSE(waypoint_danger_crosshair)
532 {
533         WarpZone_crosshair_trace(this);
534         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
535         if (wp) WaypointSprite_Ping(wp);
536         sprint(this, "DANGER spawned at crosshair\n");
537 }
538
539 IMPULSE(waypoint_danger_death)
540 {
541         if (!this.death_origin) return;
542         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
543         if (wp) WaypointSprite_Ping(wp);
544         sprint(this, "DANGER spawned at death location\n");
545 }
546
547 IMPULSE(waypoint_clear_personal)
548 {
549         WaypointSprite_ClearPersonal(this);
550         if (this.personal)
551         {
552                 delete(this.personal);
553                 this.personal = NULL;
554
555                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
556                         ClientKill(this);
557         }
558         sprint(this, "personal waypoint cleared\n");
559 }
560
561 IMPULSE(waypoint_clear)
562 {
563         WaypointSprite_ClearOwned(this);
564         if (this.personal)
565         {
566                 delete(this.personal);
567                 this.personal = NULL;
568                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
569                         ClientKill(this);
570         }
571         sprint(this, "all waypoints cleared\n");
572 }
573
574 IMPULSE(navwaypoint_spawn)
575 {
576         if (!autocvar_g_waypointeditor) return;
577         waypoint_spawn_fromeditor(this);
578 }
579
580 IMPULSE(navwaypoint_remove)
581 {
582         if (!autocvar_g_waypointeditor) return;
583         waypoint_remove_fromeditor(this);
584 }
585
586 IMPULSE(navwaypoint_relink)
587 {
588         if (!autocvar_g_waypointeditor) return;
589         waypoint_schedulerelinkall();
590 }
591
592 IMPULSE(navwaypoint_save)
593 {
594         if (!autocvar_g_waypointeditor) return;
595         waypoint_saveall();
596 }
597
598 IMPULSE(navwaypoint_unreachable)
599 {
600         if (!autocvar_g_waypointeditor) return;
601         waypoint_unreachable(this);
602 }