]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/impulse.qc
Merge branch 'master' into terencehill/lms_updates
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / impulse.qc
1 #include "impulse.qh"
2
3 #include <common/gamemodes/_mod.qh>
4 #include <common/minigames/sv_minigames.qh>
5 #include <common/mutators/mutator/waypoints/waypointsprites.qh>
6 #include <common/state.qh>
7 #include <common/vehicles/sv_vehicles.qh>
8 #include <common/weapons/_all.qh>
9 #include <server/cheats.qh>
10 #include <server/client.qh>
11 #include <server/clientkill.qh>
12 #include <server/command/common.qh>
13 #include <server/damage.qh>
14 #include <server/mutators/_mod.qh>
15 #include <server/round_handler.qh>
16 #include <server/weapons/selection.qh>
17 #include <server/weapons/throwing.qh>
18 #include <server/weapons/tracing.qh>
19 #include <server/weapons/weaponsystem.qh>
20
21 .entity vehicle;
22
23 #define IMPULSE(id) _IMPULSE(IMP_##id)
24 #define _IMPULSE(id) \
25         void id##_handle(entity this); \
26         STATIC_INIT_LATE(id) \
27         { \
28                 id.impulse_handle = id##_handle; \
29         } \
30         void id##_handle(entity this)
31
32 /**
33  * Impulse map:
34  *
35  * 0 reserved (no input)
36  *
37  * 99: loaded
38  *
39  * 140: moving clone
40  * 141: ctf speedrun
41  * 142: fixed clone
42  * 143: emergency teleport
43  * 148: unfairly eliminate
44  *
45  * TODO:
46  * 200 to 209: prev weapon shortcuts
47  * 210 to 219: best weapon shortcuts
48  * 220 to 229: next weapon shortcuts
49  * 230 to 253: individual weapons (up to 24)
50  */
51
52 // weapon switching impulses
53
54 void weapon_group_handle(entity this, int number, int imp)
55 {
56         if (IS_DEAD(this))
57         {
58                 this.impulse = imp;
59                 return;
60         }
61         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
62         {
63                 .entity weaponentity = weaponentities[slot];
64                 W_NextWeaponOnImpulse(this, number, weaponentity);
65                 if(autocvar_g_weaponswitch_debug != 1)
66                         break;
67         }
68 }
69
70 #define X(i) \
71         IMPULSE(weapon_group_##i) \
72         { \
73                 weapon_group_handle(this, i, IMP_weapon_group_##i.impulse); \
74         }
75 X(1)
76 X(2)
77 X(3)
78 X(4)
79 X(5)
80 X(6)
81 X(7)
82 X(8)
83 X(9)
84 X(0)
85 #undef X
86
87 // custom order weapon cycling
88
89 void weapon_priority_handle(entity this, int dir, int number, int imp)
90 {
91         if (this.vehicle) return;
92         if (IS_DEAD(this))
93         {
94                 this.impulse = imp;
95                 return;
96         }
97         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
98         {
99                 .entity weaponentity = weaponentities[slot];
100                 W_CycleWeapon(this, CS_CVAR(this).cvar_cl_weaponpriorities[number], dir, weaponentity);
101                 if(autocvar_g_weaponswitch_debug != 1)
102                         break;
103         }
104 }
105
106 #define X(i, dir) \
107         IMPULSE(weapon_priority_##i##_##dir) \
108         { \
109                 noref int prev = -1; \
110                 noref int best =  0; \
111                 noref int next = +1; \
112                 weapon_priority_handle(this, dir, i, IMP_weapon_priority_##i##_##dir.impulse); \
113         }
114 X(0, prev)
115 X(1, prev)
116 X(2, prev)
117 X(3, prev)
118 X(4, prev)
119 X(5, prev)
120 X(6, prev)
121 X(7, prev)
122 X(8, prev)
123 X(9, prev)
124
125 X(0, best)
126 X(1, best)
127 X(2, best)
128 X(3, best)
129 X(4, best)
130 X(5, best)
131 X(6, best)
132 X(7, best)
133 X(8, best)
134 X(9, best)
135
136 X(0, next)
137 X(1, next)
138 X(2, next)
139 X(3, next)
140 X(4, next)
141 X(5, next)
142 X(6, next)
143 X(7, next)
144 X(8, next)
145 X(9, next)
146 #undef X
147
148 // direct weapons
149
150 void weapon_byid_handle(entity this, int number, int imp)
151 {
152         if (this.vehicle) return;
153         if (IS_DEAD(this))
154         {
155                 this.impulse = imp;
156                 return;
157         }
158         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
159         {
160                 .entity weaponentity = weaponentities[slot];
161                 W_SwitchWeapon_TryOthers(this, REGISTRY_GET(Weapons, WEP_FIRST + number), weaponentity);
162                 if(autocvar_g_weaponswitch_debug != 1)
163                         break;
164         }
165 }
166
167 #define X(i) \
168         IMPULSE(weapon_byid_##i) \
169         { \
170                 weapon_byid_handle(this, i, IMP_weapon_byid_##i.impulse); \
171         }
172 X(0)
173 X(1)
174 X(2)
175 X(3)
176 X(4)
177 X(5)
178 X(6)
179 X(7)
180 X(8)
181 X(9)
182 X(10)
183 X(11)
184 X(12)
185 X(13)
186 X(14)
187 X(15)
188 X(16)
189 X(17)
190 X(18)
191 X(19)
192 X(20)
193 X(21)
194 X(22)
195 X(23)
196 #undef X
197
198 IMPULSE(weapon_next_byid)
199 {
200         if (this.vehicle) return;
201         if (IS_DEAD(this))
202         {
203                 this.impulse = IMP_weapon_next_byid.impulse;
204                 return;
205         }
206         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
207         {
208                 .entity weaponentity = weaponentities[slot];
209                 W_NextWeapon(this, 0, weaponentity);
210
211                 if(autocvar_g_weaponswitch_debug != 1)
212                         break;
213         }
214 }
215
216 IMPULSE(weapon_prev_byid)
217 {
218         if (this.vehicle) return;
219         if (IS_DEAD(this))
220         {
221                 this.impulse = IMP_weapon_prev_byid.impulse;
222                 return;
223         }
224         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
225         {
226                 .entity weaponentity = weaponentities[slot];
227                 W_PreviousWeapon(this, 0, weaponentity);
228
229                 if(autocvar_g_weaponswitch_debug != 1)
230                         break;
231         }
232 }
233
234 IMPULSE(weapon_next_bygroup)
235 {
236         if (this.vehicle) return;
237         if (IS_DEAD(this))
238         {
239                 this.impulse = IMP_weapon_next_bygroup.impulse;
240                 return;
241         }
242         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
243         {
244                 .entity weaponentity = weaponentities[slot];
245                 W_NextWeapon(this, 1, weaponentity);
246
247                 if(autocvar_g_weaponswitch_debug != 1)
248                         break;
249         }
250 }
251
252 IMPULSE(weapon_prev_bygroup)
253 {
254         if (this.vehicle) return;
255         if (IS_DEAD(this))
256         {
257                 this.impulse = IMP_weapon_prev_bygroup.impulse;
258                 return;
259         }
260         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
261         {
262                 .entity weaponentity = weaponentities[slot];
263                 W_PreviousWeapon(this, 1, weaponentity);
264
265                 if(autocvar_g_weaponswitch_debug != 1)
266                         break;
267         }
268 }
269
270 IMPULSE(weapon_next_bypriority)
271 {
272         if (this.vehicle) return;
273         if (IS_DEAD(this))
274         {
275                 this.impulse = IMP_weapon_next_bypriority.impulse;
276                 return;
277         }
278         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
279         {
280                 .entity weaponentity = weaponentities[slot];
281                 W_NextWeapon(this, 2, weaponentity);
282
283                 if(autocvar_g_weaponswitch_debug != 1)
284                         break;
285         }
286 }
287
288 IMPULSE(weapon_prev_bypriority)
289 {
290         if (this.vehicle) return;
291         if (IS_DEAD(this))
292         {
293                 this.impulse = IMP_weapon_prev_bypriority.impulse;
294                 return;
295         }
296         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
297         {
298                 .entity weaponentity = weaponentities[slot];
299                 W_PreviousWeapon(this, 2, weaponentity);
300
301                 if(autocvar_g_weaponswitch_debug != 1)
302                         break;
303         }
304 }
305
306 IMPULSE(weapon_last)
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_LastWeapon(this, weaponentity);
314
315                 if(autocvar_g_weaponswitch_debug != 1)
316                         break;
317         }
318 }
319
320 IMPULSE(weapon_best)
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_SwitchWeapon(this, w_getbestweapon(this, weaponentity), weaponentity);
328
329                 if(autocvar_g_weaponswitch_debug != 1)
330                         break;
331         }
332 }
333
334 IMPULSE(weapon_drop)
335 {
336         if (this.vehicle) return;
337         if (IS_DEAD(this)) return;
338         bool is_dualwielding = W_DualWielding(this);
339         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
340         {
341                 .entity weaponentity = weaponentities[slot];
342                 vector md = this.(weaponentity).movedir;
343                 vector vecs = ((md.x > 0) ? md : '0 0 0');
344                 vector dv = v_right * -vecs.y;
345                 if(!is_dualwielding)
346                         dv = '0 0 0'; // don't override!
347                 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), dv, true);
348
349                 if(autocvar_g_weaponswitch_debug == 2)
350                         break; // in this mode, the off-hand weapon is selected based on the primary weapon, don't drop it twice!
351         }
352 }
353
354 IMPULSE(weapon_reload)
355 {
356         if (this.vehicle) return;
357         if (IS_DEAD(this)) return;
358         if (weaponLocked(this)) return;
359         entity actor = this;
360         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
361         {
362                 .entity weaponentity = weaponentities[slot];
363                 Weapon w = this.(weaponentity).m_weapon;
364                 w.wr_reload(w, actor, weaponentity);
365
366                 // allow reloading all active slots?
367                 //if(autocvar_g_weaponswitch_debug != 1)
368                         //break;
369         }
370 }
371
372 void ImpulseCommands(entity this)
373 {
374         if (game_stopped) return;
375
376         int imp = CS(this).impulse;
377         if (!imp) return;
378         CS(this).impulse = 0;
379
380         if (MinigameImpulse(this, imp)) return;
381
382         if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
383
384         // allow only weapon change impulses when not in round time
385         if (round_handler_IsActive() && !round_handler_IsRoundStarted())
386         {
387                 #define X(id) case IMP_##id.impulse:
388                 switch (imp)
389                 {
390                         X(weapon_group_0)
391                         X(weapon_group_1)
392                         X(weapon_group_2)
393                         X(weapon_group_3)
394                         X(weapon_group_4)
395                         X(weapon_group_5)
396                         X(weapon_group_6)
397                         X(weapon_group_7)
398                         X(weapon_group_8)
399                         X(weapon_group_9)
400                         X(weapon_next_byid)
401                         X(weapon_prev_byid)
402                         X(weapon_next_bygroup)
403                         X(weapon_prev_bygroup)
404                         X(weapon_next_bypriority)
405                         X(weapon_prev_bypriority)
406                         X(weapon_last)
407                         X(weapon_best)
408                         X(weapon_reload)
409                         X(weapon_priority_0_prev)
410             X(weapon_priority_1_prev)
411             X(weapon_priority_2_prev)
412             X(weapon_priority_3_prev)
413             X(weapon_priority_4_prev)
414             X(weapon_priority_5_prev)
415             X(weapon_priority_6_prev)
416             X(weapon_priority_7_prev)
417             X(weapon_priority_8_prev)
418             X(weapon_priority_9_prev)
419             X(weapon_priority_0_next)
420                         X(weapon_priority_1_next)
421                         X(weapon_priority_2_next)
422                         X(weapon_priority_3_next)
423                         X(weapon_priority_4_next)
424                         X(weapon_priority_5_next)
425                         X(weapon_priority_6_next)
426                         X(weapon_priority_7_next)
427                         X(weapon_priority_8_next)
428                         X(weapon_priority_9_next)
429                         X(weapon_priority_0_best)
430             X(weapon_priority_1_best)
431             X(weapon_priority_2_best)
432             X(weapon_priority_3_best)
433             X(weapon_priority_4_best)
434             X(weapon_priority_5_best)
435             X(weapon_priority_6_best)
436             X(weapon_priority_7_best)
437             X(weapon_priority_8_best)
438             X(weapon_priority_9_best)
439             X(weapon_byid_0)
440             X(weapon_byid_1)
441             X(weapon_byid_2)
442             X(weapon_byid_3)
443             X(weapon_byid_4)
444             X(weapon_byid_5)
445             X(weapon_byid_6)
446             X(weapon_byid_7)
447             X(weapon_byid_8)
448             X(weapon_byid_9)
449             X(weapon_byid_10)
450             X(weapon_byid_11)
451             X(weapon_byid_12)
452             X(weapon_byid_13)
453             X(weapon_byid_14)
454             X(weapon_byid_15)
455             X(weapon_byid_16)
456             X(weapon_byid_17)
457             X(weapon_byid_18)
458             X(weapon_byid_19)
459             X(weapon_byid_20)
460             X(weapon_byid_21)
461             X(weapon_byid_22)
462             X(weapon_byid_23)
463                         break;
464                         default: return;
465                 }
466 #undef X
467         }
468
469         if (vehicle_impulse(this, imp)) return;
470
471         if (CheatImpulse(this, imp)) return;
472
473         FOREACH(IMPULSES, it.impulse == imp, {
474                 void(entity) f = it.impulse_handle;
475                 if (!f) continue;
476                 f(this);
477                 return;
478         });
479 }
480
481 IMPULSE(use)
482 {
483         PlayerUseKey(this);
484 }
485
486 IMPULSE(waypoint_personal_here)
487 {
488         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
489         if (wp) WaypointSprite_Ping(wp);
490         sprint(this, "personal waypoint spawned at location\n");
491 }
492
493 IMPULSE(waypoint_personal_crosshair)
494 {
495         WarpZone_crosshair_trace(this);
496         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
497         if (wp) WaypointSprite_Ping(wp);
498         sprint(this, "personal waypoint spawned at crosshair\n");
499 }
500
501 IMPULSE(waypoint_personal_death)
502 {
503         if (!this.death_origin) return;
504         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
505         if (wp) WaypointSprite_Ping(wp);
506         sprint(this, "personal waypoint spawned at death location\n");
507 }
508
509 IMPULSE(waypoint_here_follow)
510 {
511         if (!teamplay) return;
512         if (IS_DEAD(this)) return;
513         if (!MUTATOR_CALLHOOK(HelpMePing, this))
514         {
515                 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
516                 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
517                 else WaypointSprite_Ping(wp);
518         }
519         sprint(this, "HELP ME attached\n");
520 }
521
522 IMPULSE(waypoint_here_here)
523 {
524         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
525         if (wp) WaypointSprite_Ping(wp);
526         sprint(this, "HERE spawned at location\n");
527 }
528
529 IMPULSE(waypoint_here_crosshair)
530 {
531         WarpZone_crosshair_trace_plusvisibletriggers(this);
532         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
533         if (wp) WaypointSprite_Ping(wp);
534         sprint(this, "HERE spawned at crosshair\n");
535 }
536
537 IMPULSE(waypoint_here_death)
538 {
539         if (!this.death_origin) return;
540         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
541         if (wp) WaypointSprite_Ping(wp);
542         sprint(this, "HERE spawned at death location\n");
543 }
544
545 IMPULSE(waypoint_danger_here)
546 {
547         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
548         if (wp) WaypointSprite_Ping(wp);
549         sprint(this, "DANGER spawned at location\n");
550 }
551
552 IMPULSE(waypoint_danger_crosshair)
553 {
554         WarpZone_crosshair_trace(this);
555         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
556         if (wp) WaypointSprite_Ping(wp);
557         sprint(this, "DANGER spawned at crosshair\n");
558 }
559
560 IMPULSE(waypoint_danger_death)
561 {
562         if (!this.death_origin) return;
563         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
564         if (wp) WaypointSprite_Ping(wp);
565         sprint(this, "DANGER spawned at death location\n");
566 }
567
568 IMPULSE(waypoint_clear_personal)
569 {
570         WaypointSprite_ClearPersonal(this);
571         if (this.personal)
572         {
573                 delete(this.personal);
574                 this.personal = NULL;
575
576                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
577                         ClientKill(this);
578         }
579         sprint(this, "personal waypoint cleared\n");
580 }
581
582 IMPULSE(waypoint_clear)
583 {
584         WaypointSprite_ClearOwned(this);
585         if (this.personal)
586         {
587                 delete(this.personal);
588                 this.personal = NULL;
589                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
590                         ClientKill(this);
591         }
592         sprint(this, "all waypoints cleared\n");
593 }