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 vecs = ((md.x > 0) ? md : '0 0 0');
344 vector dv = v_right * -vecs.y;
346 dv = '0 0 0'; // don't override!
347 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), dv, true);
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!
354 IMPULSE(weapon_reload)
356 if (this.vehicle) return;
357 if (IS_DEAD(this)) return;
358 if (weaponLocked(this)) return;
360 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
362 .entity weaponentity = weaponentities[slot];
363 Weapon w = this.(weaponentity).m_weapon;
364 w.wr_reload(w, actor, weaponentity);
366 // allow reloading all active slots?
367 //if(autocvar_g_weaponswitch_debug != 1)
372 void ImpulseCommands(entity this)
374 if (game_stopped) return;
376 int imp = CS(this).impulse;
378 CS(this).impulse = 0;
380 if (MinigameImpulse(this, imp)) return;
382 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
384 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
386 // impulses forbidden while waiting for the start of a round
387 #define X(id) case IMP_##id.impulse:
398 if (vehicle_impulse(this, imp)) return;
400 if (CheatImpulse(this, imp)) return;
402 FOREACH(IMPULSES, it.impulse == imp, {
403 void(entity) f = it.impulse_handle;
415 IMPULSE(waypoint_personal_here)
417 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
418 if (wp) WaypointSprite_Ping(wp);
419 sprint(this, "personal waypoint spawned at location\n");
422 IMPULSE(waypoint_personal_crosshair)
424 WarpZone_crosshair_trace(this);
425 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
426 if (wp) WaypointSprite_Ping(wp);
427 sprint(this, "personal waypoint spawned at crosshair\n");
430 IMPULSE(waypoint_personal_death)
432 if (!this.death_origin) return;
433 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
434 if (wp) WaypointSprite_Ping(wp);
435 sprint(this, "personal waypoint spawned at death location\n");
438 IMPULSE(waypoint_here_follow)
440 if (!teamplay) return;
441 if (IS_DEAD(this)) return;
442 if (!MUTATOR_CALLHOOK(HelpMePing, this))
444 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
445 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
446 else WaypointSprite_Ping(wp);
448 sprint(this, "HELP ME attached\n");
451 IMPULSE(waypoint_here_here)
453 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
454 if (wp) WaypointSprite_Ping(wp);
455 sprint(this, "HERE spawned at location\n");
458 IMPULSE(waypoint_here_crosshair)
460 WarpZone_crosshair_trace_plusvisibletriggers(this);
461 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
462 if (wp) WaypointSprite_Ping(wp);
463 sprint(this, "HERE spawned at crosshair\n");
466 IMPULSE(waypoint_here_death)
468 if (!this.death_origin) return;
469 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
470 if (wp) WaypointSprite_Ping(wp);
471 sprint(this, "HERE spawned at death location\n");
474 IMPULSE(waypoint_danger_here)
476 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
477 if (wp) WaypointSprite_Ping(wp);
478 sprint(this, "DANGER spawned at location\n");
481 IMPULSE(waypoint_danger_crosshair)
483 WarpZone_crosshair_trace(this);
484 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
485 if (wp) WaypointSprite_Ping(wp);
486 sprint(this, "DANGER spawned at crosshair\n");
489 IMPULSE(waypoint_danger_death)
491 if (!this.death_origin) return;
492 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
493 if (wp) WaypointSprite_Ping(wp);
494 sprint(this, "DANGER spawned at death location\n");
497 IMPULSE(waypoint_clear_personal)
499 WaypointSprite_ClearPersonal(this);
502 delete(this.personal);
503 this.personal = NULL;
505 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
508 sprint(this, "personal waypoint cleared\n");
511 IMPULSE(waypoint_clear)
513 WaypointSprite_ClearOwned(this);
516 delete(this.personal);
517 this.personal = NULL;
518 if((g_cts || g_race) && autocvar_g_allow_checkpoints)
521 sprint(this, "all waypoints cleared\n");