]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/impulse.qc
Update default video settings
[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 dv = v_right * -md.y;
344                 if(!is_dualwielding)
345                         dv = '0 0 0'; // don't override!
346                 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), dv, true);
347
348                 if(autocvar_g_weaponswitch_debug == 2)
349                         break; // in this mode, the off-hand weapon is selected based on the primary weapon, don't drop it twice!
350         }
351 }
352
353 IMPULSE(weapon_reload)
354 {
355         if (this.vehicle) return;
356         if (IS_DEAD(this)) return;
357         if (weaponLocked(this)) return;
358         entity actor = this;
359         for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
360         {
361                 .entity weaponentity = weaponentities[slot];
362                 Weapon w = this.(weaponentity).m_weapon;
363                 w.wr_reload(w, actor, weaponentity);
364
365                 // allow reloading all active slots?
366                 //if(autocvar_g_weaponswitch_debug != 1)
367                         //break;
368         }
369 }
370
371 void ImpulseCommands(entity this)
372 {
373         if (game_stopped) return;
374
375         int imp = CS(this).impulse;
376         if (!imp) return;
377         CS(this).impulse = 0;
378
379         if (MinigameImpulse(this, imp)) return;
380
381         if (timeout_status == TIMEOUT_ACTIVE) return;  // don't allow any impulses while the game is paused
382
383         if (round_handler_IsActive() && !round_handler_IsRoundStarted())
384         {
385                 // impulses forbidden while waiting for the start of a round
386                 #define X(id) case IMP_##id.impulse:
387                 switch (imp)
388                 {
389                         X(weapon_drop)
390                         X(weapon_reload)
391                         X(use)
392                                 return;
393                 }
394 #undef X
395         }
396
397         if (vehicle_impulse(this, imp)) return;
398
399         if (CheatImpulse(this, imp)) return;
400
401         FOREACH(IMPULSES, it.impulse == imp, {
402                 void(entity) f = it.impulse_handle;
403                 if (!f) continue;
404                 f(this);
405                 return;
406         });
407 }
408
409 IMPULSE(use)
410 {
411         PlayerUseKey(this);
412 }
413
414 IMPULSE(waypoint_personal_here)
415 {
416         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
417         if (wp) WaypointSprite_Ping(wp);
418         sprint(this, "personal waypoint spawned at location\n");
419 }
420
421 IMPULSE(waypoint_personal_crosshair)
422 {
423         WarpZone_crosshair_trace(this);
424         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
425         if (wp) WaypointSprite_Ping(wp);
426         sprint(this, "personal waypoint spawned at crosshair\n");
427 }
428
429 IMPULSE(waypoint_personal_death)
430 {
431         if (!this.death_origin) return;
432         entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
433         if (wp) WaypointSprite_Ping(wp);
434         sprint(this, "personal waypoint spawned at death location\n");
435 }
436
437 IMPULSE(waypoint_here_follow)
438 {
439         if (!teamplay) return;
440         if (IS_DEAD(this)) return;
441         if (!MUTATOR_CALLHOOK(HelpMePing, this))
442         {
443                 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
444                 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
445                 else WaypointSprite_Ping(wp);
446         }
447         sprint(this, "HELP ME attached\n");
448 }
449
450 IMPULSE(waypoint_here_here)
451 {
452         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
453         if (wp) WaypointSprite_Ping(wp);
454         sprint(this, "HERE spawned at location\n");
455 }
456
457 IMPULSE(waypoint_here_crosshair)
458 {
459         WarpZone_crosshair_trace_plusvisibletriggers(this);
460         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
461         if (wp) WaypointSprite_Ping(wp);
462         sprint(this, "HERE spawned at crosshair\n");
463 }
464
465 IMPULSE(waypoint_here_death)
466 {
467         if (!this.death_origin) return;
468         entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
469         if (wp) WaypointSprite_Ping(wp);
470         sprint(this, "HERE spawned at death location\n");
471 }
472
473 IMPULSE(waypoint_danger_here)
474 {
475         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
476         if (wp) WaypointSprite_Ping(wp);
477         sprint(this, "DANGER spawned at location\n");
478 }
479
480 IMPULSE(waypoint_danger_crosshair)
481 {
482         WarpZone_crosshair_trace(this);
483         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
484         if (wp) WaypointSprite_Ping(wp);
485         sprint(this, "DANGER spawned at crosshair\n");
486 }
487
488 IMPULSE(waypoint_danger_death)
489 {
490         if (!this.death_origin) return;
491         entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
492         if (wp) WaypointSprite_Ping(wp);
493         sprint(this, "DANGER spawned at death location\n");
494 }
495
496 IMPULSE(waypoint_clear_personal)
497 {
498         WaypointSprite_ClearPersonal(this);
499         if (this.personal)
500         {
501                 delete(this.personal);
502                 this.personal = NULL;
503
504                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
505                         ClientKill(this);
506         }
507         sprint(this, "personal waypoint cleared\n");
508 }
509
510 IMPULSE(waypoint_clear)
511 {
512         WaypointSprite_ClearOwned(this);
513         if (this.personal)
514         {
515                 delete(this.personal);
516                 this.personal = NULL;
517                 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
518                         ClientKill(this);
519         }
520         sprint(this, "all waypoints cleared\n");
521 }