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