]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/impulse.qc
Merge branch 'master' into Mario/wepent_experimental
[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 bool autocvar_g_weaponswitch_debug;
56
57 #define X(slot) \
58         IMPULSE(weapon_group_##slot) \
59         { \
60                 if (IS_DEAD(this)) \
61                 { \
62                         this.impulse = IMP_weapon_group_##slot.impulse; \
63                         return; \
64                 } \
65                 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
66                 { \
67                         .entity weaponentity = weaponentities[wepslot]; \
68                         W_NextWeaponOnImpulse(this, slot, weaponentity); \
69                         if(wepslot == 0 && !autocvar_g_weaponswitch_debug) \
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(slot, dir) \
88         IMPULSE(weapon_priority_##slot##_##dir) \
89         { \
90                 if (this.vehicle) return; \
91                 if (IS_DEAD(this)) \
92                 { \
93                         this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
94                         return; \
95                 } \
96                 noref int prev = -1; \
97                 noref int best =  0; \
98                 noref int next = +1; \
99                 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
100                 { \
101                         .entity weaponentity = weaponentities[wepslot]; \
102                         W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir, weaponentity); \
103                         if(wepslot == 0 && !autocvar_g_weaponswitch_debug) \
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(this, Weapons_from(WEP_FIRST + i), weaponentity); \
156                         if(slot == 0 && !autocvar_g_weaponswitch_debug) \
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
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(slot == 0 && !autocvar_g_weaponswitch_debug)
318                         break;
319         }
320 }
321
322 IMPULSE(weapon_drop)
323 {
324         if (this.vehicle) return;
325         if (IS_DEAD(this)) return;
326         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
327         {
328                 .entity weaponentity = weaponentities[slot];
329                 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
330
331                 if(slot == 0 && !autocvar_g_weaponswitch_debug)
332                         break;
333         }
334 }
335
336 IMPULSE(weapon_reload)
337 {
338         if (this.vehicle) return;
339         if (IS_DEAD(this)) return;
340         if (forbidWeaponUse(this)) return;
341         entity actor = this;
342         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
343         {
344                 .entity weaponentity = weaponentities[slot];
345                 Weapon w = this.(weaponentity).m_weapon;
346                 w.wr_reload(w, actor, weaponentity);
347
348                 if(slot == 0 && !autocvar_g_weaponswitch_debug)
349                         break;
350         }
351 }
352
353 void ImpulseCommands(entity this)
354 {
355         if (gameover) return;
356
357         int imp = this.impulse;
358         if (!imp) return;
359         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(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         sprint(this, "personal waypoint cleared\n");
558 }
559
560 IMPULSE(waypoint_clear)
561 {
562         WaypointSprite_ClearOwned(this);
563         if (this.personal)
564         {
565                 delete(this.personal);
566                 this.personal = NULL;
567         }
568         sprint(this, "all waypoints cleared\n");
569 }
570
571 IMPULSE(navwaypoint_spawn)
572 {
573         if (!autocvar_g_waypointeditor) return;
574         waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
575         bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
576 }
577
578 IMPULSE(navwaypoint_remove)
579 {
580         if (!autocvar_g_waypointeditor) return;
581         entity e = navigation_findnearestwaypoint(this, false);
582         if (!e) return;
583         if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
584         bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
585         waypoint_remove(e);
586 }
587
588 IMPULSE(navwaypoint_relink)
589 {
590         if (!autocvar_g_waypointeditor) return;
591         waypoint_schedulerelinkall();
592 }
593
594 IMPULSE(navwaypoint_save)
595 {
596         if (!autocvar_g_waypointeditor) return;
597         waypoint_saveall();
598 }
599
600 IMPULSE(navwaypoint_unreachable)
601 {
602         if (!autocvar_g_waypointeditor) return;
603         IL_EACH(g_waypoints, true,
604         {
605                 it.colormod = '0.5 0.5 0.5';
606                 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
607         });
608         entity e2 = navigation_findnearestwaypoint(this, false);
609         navigation_markroutes(this, e2);
610
611         int j, m;
612
613         j = 0;
614         m = 0;
615         IL_EACH(g_waypoints, it.wpcost >= 10000000,
616         {
617                 LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
618                 it.colormod_z = 8;
619                 it.effects |= EF_NODEPTHTEST | EF_BLUE;
620                 ++j;
621                 ++m;
622         });
623         if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
624         navigation_markroutes_inverted(e2);
625
626         j = 0;
627         IL_EACH(g_waypoints, it.wpcost >= 10000000,
628         {
629                 LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
630                 it.colormod_x = 8;
631                 if (!(it.effects & EF_NODEPTHTEST))  // not already reported before
632                         ++m;
633                 it.effects |= EF_NODEPTHTEST | EF_RED;
634                 ++j;
635         });
636         if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
637         if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
638
639         j = 0;
640         IL_EACH(g_spawnpoints, true,
641         {
642                 vector org = it.origin;
643                 tracebox(it.origin, PL_MIN_CONST, PL_MAX_CONST, it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
644                 setorigin(it, trace_endpos);
645                 if (navigation_findnearestwaypoint(it, false))
646                 {
647                         setorigin(it, org);
648                         it.effects &= ~EF_NODEPTHTEST;
649                         it.model = "";
650                 }
651                 else
652                 {
653                         setorigin(it, org);
654                         LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
655                         it.effects |= EF_NODEPTHTEST;
656                         _setmodel(it, this.model);
657                         it.frame = this.frame;
658                         it.skin = this.skin;
659                         it.colormod = '8 0.5 8';
660                         setsize(it, '0 0 0', '0 0 0');
661                         ++j;
662                 }
663         });
664         if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
665
666         j = 0;
667         IL_EACH(g_items, true,
668         {
669                 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
670                 it.colormod = '0.5 0.5 0.5';
671         });
672         IL_EACH(g_items, true,
673         {
674                 if (navigation_findnearestwaypoint(it, false)) continue;
675                 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
676                 it.effects |= EF_NODEPTHTEST | EF_RED;
677                 it.colormod_x = 8;
678                 ++j;
679         });
680         if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
681
682         j = 0;
683         IL_EACH(g_items, true,
684         {
685                 if (navigation_findnearestwaypoint(it, true)) continue;
686                 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
687                 it.effects |= EF_NODEPTHTEST | EF_BLUE;
688                 it.colormod_z = 8;
689                 ++j;
690         });
691         if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);
692 }