]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/impulse.qc
Merge branch 'master' into martin-t/damagetext
[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, 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 = this.impulse;
356         if (!imp) return;
357         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_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
578         bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
579 }
580
581 IMPULSE(navwaypoint_remove)
582 {
583         if (!autocvar_g_waypointeditor) return;
584         entity e = navigation_findnearestwaypoint(this, false);
585         if (!e) return;
586         if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
587         bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
588         waypoint_remove(e);
589 }
590
591 IMPULSE(navwaypoint_relink)
592 {
593         if (!autocvar_g_waypointeditor) return;
594         waypoint_schedulerelinkall();
595 }
596
597 IMPULSE(navwaypoint_save)
598 {
599         if (!autocvar_g_waypointeditor) return;
600         waypoint_saveall();
601 }
602
603 IMPULSE(navwaypoint_unreachable)
604 {
605         if (!autocvar_g_waypointeditor) return;
606         IL_EACH(g_waypoints, true,
607         {
608                 it.colormod = '0.5 0.5 0.5';
609                 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
610         });
611         entity e2 = navigation_findnearestwaypoint(this, false);
612         navigation_markroutes(this, e2);
613
614         int j, m;
615
616         j = 0;
617         m = 0;
618         IL_EACH(g_waypoints, it.wpcost >= 10000000,
619         {
620                 LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
621                 it.colormod_z = 8;
622                 it.effects |= EF_NODEPTHTEST | EF_BLUE;
623                 ++j;
624                 ++m;
625         });
626         if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
627         navigation_markroutes_inverted(e2);
628
629         j = 0;
630         IL_EACH(g_waypoints, it.wpcost >= 10000000,
631         {
632                 LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
633                 it.colormod_x = 8;
634                 if (!(it.effects & EF_NODEPTHTEST))  // not already reported before
635                         ++m;
636                 it.effects |= EF_NODEPTHTEST | EF_RED;
637                 ++j;
638         });
639         if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
640         if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
641
642         j = 0;
643         IL_EACH(g_spawnpoints, true,
644         {
645                 vector org = it.origin;
646                 tracebox(it.origin, PL_MIN_CONST, PL_MAX_CONST, it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
647                 setorigin(it, trace_endpos);
648                 if (navigation_findnearestwaypoint(it, false))
649                 {
650                         setorigin(it, org);
651                         it.effects &= ~EF_NODEPTHTEST;
652                         it.model = "";
653                 }
654                 else
655                 {
656                         setorigin(it, org);
657                         LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
658                         it.effects |= EF_NODEPTHTEST;
659                         _setmodel(it, this.model);
660                         it.frame = this.frame;
661                         it.skin = this.skin;
662                         it.colormod = '8 0.5 8';
663                         setsize(it, '0 0 0', '0 0 0');
664                         ++j;
665                 }
666         });
667         if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
668
669         j = 0;
670         IL_EACH(g_items, true,
671         {
672                 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
673                 it.colormod = '0.5 0.5 0.5';
674         });
675         IL_EACH(g_items, true,
676         {
677                 if (navigation_findnearestwaypoint(it, false)) continue;
678                 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
679                 it.effects |= EF_NODEPTHTEST | EF_RED;
680                 it.colormod_x = 8;
681                 ++j;
682         });
683         if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
684
685         j = 0;
686         IL_EACH(g_items, true,
687         {
688                 if (navigation_findnearestwaypoint(it, true)) continue;
689                 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
690                 it.effects |= EF_NODEPTHTEST | EF_BLUE;
691                 it.colormod_z = 8;
692                 ++j;
693         });
694         if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
695 }