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