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>
23 #define IMPULSE(id) _IMPULSE(IMP_##id)
24 #define _IMPULSE(id) \
25 void id##_handle(entity this); \
26 STATIC_INIT_LATE(id) \
28 id.impulse_handle = id##_handle; \
30 void id##_handle(entity this)
35 * 0 reserved (no input)
42 * 143: emergency teleport
43 * 148: unfairly eliminate
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)
52 // weapon switching impulses
54 void weapon_group_handle(entity this, int number, int imp)
61 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
63 .entity weaponentity = weaponentities[slot];
64 W_NextWeaponOnImpulse(this, number, weaponentity);
65 if(autocvar_g_weaponswitch_debug != 1)
71 IMPULSE(weapon_group_##i) \
73 weapon_group_handle(this, i, IMP_weapon_group_##i.impulse); \
87 // custom order weapon cycling
89 void weapon_priority_handle(entity this, int dir, int number, int imp)
91 if (this.vehicle) return;
97 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
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)
107 IMPULSE(weapon_priority_##i##_##dir) \
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); \
150 void weapon_byid_handle(entity this, int number, int imp)
152 if (this.vehicle) return;
158 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
160 .entity weaponentity = weaponentities[slot];
161 W_SwitchWeapon_TryOthers(this, REGISTRY_GET(Weapons, WEP_FIRST + number), weaponentity);
162 if(autocvar_g_weaponswitch_debug != 1)
168 IMPULSE(weapon_byid_##i) \
170 weapon_byid_handle(this, i, IMP_weapon_byid_##i.impulse); \
198 IMPULSE(weapon_next_byid)
200 if (this.vehicle) return;
203 this.impulse = IMP_weapon_next_byid.impulse;
206 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
208 .entity weaponentity = weaponentities[slot];
209 W_NextWeapon(this, 0, weaponentity);
211 if(autocvar_g_weaponswitch_debug != 1)
216 IMPULSE(weapon_prev_byid)
218 if (this.vehicle) return;
221 this.impulse = IMP_weapon_prev_byid.impulse;
224 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
226 .entity weaponentity = weaponentities[slot];
227 W_PreviousWeapon(this, 0, weaponentity);
229 if(autocvar_g_weaponswitch_debug != 1)
234 IMPULSE(weapon_next_bygroup)
236 if (this.vehicle) return;
239 this.impulse = IMP_weapon_next_bygroup.impulse;
242 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
244 .entity weaponentity = weaponentities[slot];
245 W_NextWeapon(this, 1, weaponentity);
247 if(autocvar_g_weaponswitch_debug != 1)
252 IMPULSE(weapon_prev_bygroup)
254 if (this.vehicle) return;
257 this.impulse = IMP_weapon_prev_bygroup.impulse;
260 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
262 .entity weaponentity = weaponentities[slot];
263 W_PreviousWeapon(this, 1, weaponentity);
265 if(autocvar_g_weaponswitch_debug != 1)
270 IMPULSE(weapon_next_bypriority)
272 if (this.vehicle) return;
275 this.impulse = IMP_weapon_next_bypriority.impulse;
278 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
280 .entity weaponentity = weaponentities[slot];
281 W_NextWeapon(this, 2, weaponentity);
283 if(autocvar_g_weaponswitch_debug != 1)
288 IMPULSE(weapon_prev_bypriority)
290 if (this.vehicle) return;
293 this.impulse = IMP_weapon_prev_bypriority.impulse;
296 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
298 .entity weaponentity = weaponentities[slot];
299 W_PreviousWeapon(this, 2, weaponentity);
301 if(autocvar_g_weaponswitch_debug != 1)
308 if (this.vehicle) return;
309 if (IS_DEAD(this)) return;
310 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
312 .entity weaponentity = weaponentities[slot];
313 W_LastWeapon(this, weaponentity);
315 if(autocvar_g_weaponswitch_debug != 1)
322 if (this.vehicle) return;
323 if (IS_DEAD(this)) return;
324 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
326 .entity weaponentity = weaponentities[slot];
327 W_SwitchWeapon(this, w_getbestweapon(this, weaponentity), weaponentity);
329 if(autocvar_g_weaponswitch_debug != 1)
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)
341 .entity weaponentity = weaponentities[slot];
342 vector md = this.(weaponentity).movedir;
343 vector dv = v_right * -md.y;
345 dv = '0 0 0'; // don't override!
346 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), dv, true);
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!
353 IMPULSE(weapon_reload)
355 if (this.vehicle) return;
356 if (IS_DEAD(this)) return;
357 if (weaponLocked(this)) return;
359 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
361 .entity weaponentity = weaponentities[slot];
362 Weapon w = this.(weaponentity).m_weapon;
363 w.wr_reload(w, actor, weaponentity);
365 // allow reloading all active slots?
366 //if(autocvar_g_weaponswitch_debug != 1)
371 void ImpulseCommands(entity this)
373 if (game_stopped) return;
375 int imp = CS(this).impulse;
377 CS(this).impulse = 0;
379 if (MinigameImpulse(this, imp)) return;
381 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
383 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
385 // impulses forbidden while waiting for the start of a round
386 #define X(id) case IMP_##id.impulse:
397 if (vehicle_impulse(this, imp)) return;
399 if (CheatImpulse(this, imp)) return;
401 FOREACH(IMPULSES, it.impulse == imp, {
402 void(entity) f = it.impulse_handle;
414 IMPULSE(waypoint_personal_here)
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");
421 IMPULSE(waypoint_personal_crosshair)
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");
429 IMPULSE(waypoint_personal_death)
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");
437 IMPULSE(waypoint_here_follow)
439 if (!teamplay) return;
440 if (IS_DEAD(this)) return;
441 if (!MUTATOR_CALLHOOK(HelpMePing, this))
443 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
444 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
445 else WaypointSprite_Ping(wp);
447 sprint(this, "HELP ME attached\n");
450 IMPULSE(waypoint_here_here)
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");
457 IMPULSE(waypoint_here_crosshair)
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");
465 IMPULSE(waypoint_here_death)
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");
473 IMPULSE(waypoint_danger_here)
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");
480 IMPULSE(waypoint_danger_crosshair)
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");
488 IMPULSE(waypoint_danger_death)
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");
496 IMPULSE(waypoint_clear_personal)
498 WaypointSprite_ClearPersonal(this);
501 delete(this.personal);
502 this.personal = NULL;
504 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
507 sprint(this, "personal waypoint cleared\n");
510 IMPULSE(waypoint_clear)
512 WaypointSprite_ClearOwned(this);
515 delete(this.personal);
516 this.personal = NULL;
517 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
520 sprint(this, "all waypoints cleared\n");