]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/cl_impulse.qc
Impulses: register priority lists
[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, n) _IMPULSE(IMP_##id, n)
23 #define _IMPULSE(id, n) \
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, imp) \
54         IMPULSE(weapon_group_##slot, imp) \
55         { \
56                 if (this.deadflag != DEAD_NO) return; \
57                 W_NextWeaponOnImpulse(slot); \
58         }
59 X(1, 1)
60 X(2, 2)
61 X(3, 3)
62 X(4, 4)
63 X(5, 5)
64 X(6, 6)
65 X(7, 7)
66 X(8, 8)
67 X(9, 9)
68 X(0, 14)
69 #undef X
70
71 // custom order weapon cycling
72
73 #define X(slot, dir, imp) \
74         IMPULSE(weapon_priority_##slot##_##dir, imp) \
75         { \
76                 if (this.vehicle) return; \
77                 if (this.deadflag != DEAD_NO) return; \
78                 noref int prev = -1; \
79                 noref int best =  0; \
80                 noref int next = +1; \
81                 W_CycleWeapon(this.cvar_cl_weaponpriorities[slot], dir); \
82         }
83 X(0, prev, 200)
84 X(1, prev, 201)
85 X(2, prev, 202)
86 X(3, prev, 203)
87 X(4, prev, 204)
88 X(5, prev, 205)
89 X(6, prev, 206)
90 X(7, prev, 207)
91 X(8, prev, 208)
92 X(9, prev, 209)
93
94 X(0, best, 210)
95 X(1, best, 211)
96 X(2, best, 212)
97 X(3, best, 213)
98 X(4, best, 214)
99 X(5, best, 215)
100 X(6, best, 216)
101 X(7, best, 217)
102 X(8, best, 218)
103 X(9, best, 219)
104
105 X(0, next, 220)
106 X(1, next, 221)
107 X(2, next, 222)
108 X(3, next, 223)
109 X(4, next, 224)
110 X(5, next, 225)
111 X(6, next, 226)
112 X(7, next, 227)
113 X(8, next, 228)
114 X(9, next, 229)
115 #undef X
116
117 IMPULSE(weapon_next_byid, 10)
118 {
119         if (this.vehicle) return;
120         if (this.deadflag != DEAD_NO) return;
121         W_NextWeapon(0);
122 }
123
124 IMPULSE(weapon_prev_byid, 12)
125 {
126         if (this.vehicle) return;
127         if (this.deadflag != DEAD_NO) return;
128         W_PreviousWeapon(0);
129 }
130
131 IMPULSE(weapon_next_bygroup, 18)
132 {
133         if (this.vehicle) return;
134         if (this.deadflag != DEAD_NO) return;
135         W_NextWeapon(1);
136 }
137
138 IMPULSE(weapon_prev_bygroup, 19)
139 {
140         if (this.vehicle) return;
141         if (this.deadflag != DEAD_NO) return;
142         W_PreviousWeapon(1);
143 }
144
145 IMPULSE(weapon_next_bypriority, 15)
146 {
147         if (this.vehicle) return;
148         if (this.deadflag != DEAD_NO) return;
149         W_NextWeapon(2);
150 }
151
152 IMPULSE(weapon_prev_bypriority, 16)
153 {
154         if (this.vehicle) return;
155         if (this.deadflag != DEAD_NO) return;
156         W_PreviousWeapon(2);
157 }
158
159 IMPULSE(weapon_last, 11)
160 {
161         if (this.vehicle) return;
162         if (this.deadflag != DEAD_NO) return;
163         W_LastWeapon();
164 }
165
166 IMPULSE(weapon_best, 13)
167 {
168         if (this.vehicle) return;
169         if (this.deadflag != DEAD_NO) return;
170         W_SwitchWeapon(w_getbestweapon(this));
171 }
172
173 IMPULSE(weapon_drop, 17)
174 {
175         if (this.vehicle) return;
176         if (this.deadflag != DEAD_NO) return;
177         W_ThrowWeapon(W_CalculateProjectileVelocity(this.velocity, v_forward * 750, false), '0 0 0', true);
178 }
179
180 IMPULSE(weapon_reload, 20)
181 {
182         if (this.vehicle) return;
183         if (this.deadflag != DEAD_NO) return;
184         if (forbidWeaponUse(this)) return;
185         Weapon w = Weapons_from(this.weapon);
186         w.wr_reload(w);
187 }
188
189 void ImpulseCommands(entity this)
190 {
191         if (gameover) return;
192
193         int imp = this.impulse;
194         if (!imp) return;
195         this.impulse = 0;
196
197         if (MinigameImpulse(this, imp)) return;
198
199         if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
200
201         // allow only weapon change impulses when not in round time
202         if (round_handler_IsActive() && !round_handler_IsRoundStarted())
203         {
204                 #define X(id) case IMP_##id.impulse:
205                 switch (imp)
206                 {
207                         case WEP_IMPULSE_BEGIN <= imp && imp <= WEP_IMPULSE_END:
208                         X(weapon_group_0)
209                         X(weapon_group_1)
210                         X(weapon_group_2)
211                         X(weapon_group_3)
212                         X(weapon_group_4)
213                         X(weapon_group_5)
214                         X(weapon_group_6)
215                         X(weapon_group_7)
216                         X(weapon_group_8)
217                         X(weapon_group_9)
218                         X(weapon_next_byid)
219                         X(weapon_prev_byid)
220                         X(weapon_next_bygroup)
221                         X(weapon_prev_bygroup)
222                         X(weapon_next_bypriority)
223                         X(weapon_prev_bypriority)
224                         X(weapon_last)
225                         X(weapon_best)
226                         X(weapon_reload)
227                         X(weapon_priority_0_prev)
228             X(weapon_priority_1_prev)
229             X(weapon_priority_2_prev)
230             X(weapon_priority_3_prev)
231             X(weapon_priority_4_prev)
232             X(weapon_priority_5_prev)
233             X(weapon_priority_6_prev)
234             X(weapon_priority_7_prev)
235             X(weapon_priority_8_prev)
236             X(weapon_priority_9_prev)
237             X(weapon_priority_0_next)
238                         X(weapon_priority_1_next)
239                         X(weapon_priority_2_next)
240                         X(weapon_priority_3_next)
241                         X(weapon_priority_4_next)
242                         X(weapon_priority_5_next)
243                         X(weapon_priority_6_next)
244                         X(weapon_priority_7_next)
245                         X(weapon_priority_8_next)
246                         X(weapon_priority_9_next)
247                         X(weapon_priority_0_best)
248             X(weapon_priority_1_best)
249             X(weapon_priority_2_best)
250             X(weapon_priority_3_best)
251             X(weapon_priority_4_best)
252             X(weapon_priority_5_best)
253             X(weapon_priority_6_best)
254             X(weapon_priority_7_best)
255             X(weapon_priority_8_best)
256             X(weapon_priority_9_best)
257                         break;
258                         default: return;
259                 }
260 #undef X
261         }
262
263         if (vehicle_impulse(this, imp)) return;
264
265         if (CheatImpulse(imp)) return;
266
267         FOREACH(IMPULSES, it.impulse == imp, {
268                 void(entity) f = it.impulse_handle;
269                 if (!f) continue;
270                 f(this);
271                 return;
272         });
273
274         if (imp >= WEP_IMPULSE_BEGIN && imp <= WEP_IMPULSE_END)
275         {
276                 if (!this.vehicle && this.deadflag == DEAD_NO) W_SwitchWeapon(imp - WEP_IMPULSE_BEGIN + WEP_FIRST);
277         }
278 }
279
280 IMPULSE(use, 21)
281 {
282         PlayerUseKey();
283 }
284
285 IMPULSE(waypoint_personal_here, 30)
286 {
287         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.origin, RADARICON_WAYPOINT);
288         if (wp) WaypointSprite_Ping(wp);
289         sprint(this, "personal waypoint spawned at location\n");
290 }
291
292 IMPULSE(waypoint_personal_crosshair, 31)
293 {
294         WarpZone_crosshair_trace(this);
295         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, trace_endpos, RADARICON_WAYPOINT);
296         if (wp) WaypointSprite_Ping(wp);
297         sprint(this, "personal waypoint spawned at crosshair\n");
298 }
299
300 IMPULSE(waypoint_personal_death, 32)
301 {
302         if (!this.death_origin) return;
303         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.death_origin, RADARICON_WAYPOINT);
304         if (wp) WaypointSprite_Ping(wp);
305         sprint(this, "personal waypoint spawned at death location\n");
306 }
307
308 IMPULSE(waypoint_here_follow, 33)
309 {
310         if (!teamplay) return;
311         if (this.deadflag != DEAD_NO) return;
312         if (!MUTATOR_CALLHOOK(HelpMePing, this))
313         {
314                 entity wp = WaypointSprite_Attach(WP_Helpme, true, RADARICON_HELPME);
315                 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
316                 else WaypointSprite_Ping(wp);
317         }
318         sprint(this, "HELP ME attached\n");
319 }
320
321 IMPULSE(waypoint_here_here, 34)
322 {
323         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.origin, RADARICON_HERE);
324         if (wp) WaypointSprite_Ping(wp);
325         sprint(this, "HERE spawned at location\n");
326 }
327
328 IMPULSE(waypoint_here_crosshair, 35)
329 {
330         WarpZone_crosshair_trace(this);
331         entity wp = WaypointSprite_DeployFixed(WP_Here, false, trace_endpos, RADARICON_HERE);
332         if (wp) WaypointSprite_Ping(wp);
333         sprint(this, "HERE spawned at crosshair\n");
334 }
335
336 IMPULSE(waypoint_here_death, 36)
337 {
338         if (!this.death_origin) return;
339         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.death_origin, RADARICON_HERE);
340         if (wp) WaypointSprite_Ping(wp);
341         sprint(this, "HERE spawned at death location\n");
342 }
343
344 IMPULSE(waypoint_danger_here, 37)
345 {
346         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.origin, RADARICON_DANGER);
347         if (wp) WaypointSprite_Ping(wp);
348         sprint(this, "DANGER spawned at location\n");
349 }
350
351 IMPULSE(waypoint_danger_crosshair, 38)
352 {
353         WarpZone_crosshair_trace(this);
354         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, trace_endpos, RADARICON_DANGER);
355         if (wp) WaypointSprite_Ping(wp);
356         sprint(this, "DANGER spawned at crosshair\n");
357 }
358
359 IMPULSE(waypoint_danger_death, 39)
360 {
361         if (!this.death_origin) return;
362         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.death_origin, RADARICON_DANGER);
363         if (wp) WaypointSprite_Ping(wp);
364         sprint(this, "DANGER spawned at death location\n");
365 }
366
367 IMPULSE(waypoint_clear_personal, 47)
368 {
369         WaypointSprite_ClearPersonal();
370         if (this.personal)
371         {
372                 remove(this.personal);
373                 this.personal = NULL;
374         }
375         sprint(this, "personal waypoint cleared\n");
376 }
377
378 IMPULSE(waypoint_clear, 48)
379 {
380         WaypointSprite_ClearOwned();
381         if (this.personal)
382         {
383                 remove(this.personal);
384                 this.personal = NULL;
385         }
386         sprint(this, "all waypoints cleared\n");
387 }
388
389 IMPULSE(navwaypoint_spawn, 103)
390 {
391         if (!autocvar_g_waypointeditor) return;
392         waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
393         bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
394 }
395
396 IMPULSE(navwaypoint_remove, 104)
397 {
398         if (!autocvar_g_waypointeditor) return;
399         entity e = navigation_findnearestwaypoint(this, false);
400         if (!e) return;
401         if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
402         bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
403         waypoint_remove(e);
404 }
405
406 IMPULSE(navwaypoint_relink, 105)
407 {
408         if (!autocvar_g_waypointeditor) return;
409         waypoint_schedulerelinkall();
410 }
411
412 IMPULSE(navwaypoint_save, 106)
413 {
414         if (!autocvar_g_waypointeditor) return;
415         waypoint_saveall();
416 }
417
418 IMPULSE(navwaypoint_unreachable, 107)
419 {
420         if (!autocvar_g_waypointeditor) return;
421         for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
422         {
423                 e.colormod = '0.5 0.5 0.5';
424                 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
425         }
426         entity e2 = navigation_findnearestwaypoint(this, false);
427         navigation_markroutes(e2);
428
429         int i, m;
430
431         i = 0;
432         m = 0;
433         for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
434         {
435                 if (e.wpcost < 10000000) continue;
436                 LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
437                 e.colormod_z = 8;
438                 e.effects |= EF_NODEPTHTEST | EF_BLUE;
439                 ++i;
440                 ++m;
441         }
442         if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
443         navigation_markroutes_inverted(e2);
444
445         i = 0;
446         for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
447         {
448                 if (e.wpcost < 10000000) continue;
449                 LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
450                 e.colormod_x = 8;
451                 if (!(e.effects & EF_NODEPTHTEST))  // not already reported before
452                         ++m;
453                 e.effects |= EF_NODEPTHTEST | EF_RED;
454                 ++i;
455         }
456         if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
457         if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
458
459         i = 0;
460         for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
461         {
462                 vector org = e.origin;
463                 tracebox(e.origin, PL_MIN, PL_MAX, e.origin - '0 0 512', MOVE_NOMONSTERS, world);
464                 setorigin(e, trace_endpos);
465                 if (navigation_findnearestwaypoint(e, false))
466                 {
467                         setorigin(e, org);
468                         e.effects &= ~EF_NODEPTHTEST;
469                         e.model = "";
470                 }
471                 else
472                 {
473                         setorigin(e, org);
474                         LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
475                         e.effects |= EF_NODEPTHTEST;
476                         _setmodel(e, this.model);
477                         e.frame = this.frame;
478                         e.skin = this.skin;
479                         e.colormod = '8 0.5 8';
480                         setsize(e, '0 0 0', '0 0 0');
481                         ++i;
482                 }
483         }
484         if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
485
486         i = 0;
487         entity start = findchainflags(flags, FL_ITEM);
488         for (entity e = start; e; e = e.chain)
489         {
490                 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
491                 e.colormod = '0.5 0.5 0.5';
492         }
493         for (entity e = start; e; e = e.chain)
494         {
495                 if (navigation_findnearestwaypoint(e, false)) continue;
496                 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
497                 e.effects |= EF_NODEPTHTEST | EF_RED;
498                 e.colormod_x = 8;
499                 ++i;
500         }
501         if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
502
503         i = 0;
504         for (entity e = start; e; e = e.chain)
505         {
506                 if (navigation_findnearestwaypoint(e, true)) continue;
507                 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
508                 e.effects |= EF_NODEPTHTEST | EF_BLUE;
509                 e.colormod_z = 8;
510                 ++i;
511         }
512         if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);
513 }