2 #include "round_handler.qh"
6 #include "weapons/throwing.qh"
7 #include "command/common.qh"
9 #include "weapons/selection.qh"
10 #include "weapons/tracing.qh"
11 #include "weapons/weaponsystem.qh"
13 #include <common/state.qh>
15 #include "../common/minigames/sv_minigames.qh"
17 #include <common/weapons/_all.qh>
18 #include "../common/vehicles/sv_vehicles.qh"
20 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
24 #define IMPULSE(id) _IMPULSE(IMP_##id)
25 #define _IMPULSE(id) \
26 void id##_handle(entity this); \
27 STATIC_INIT_LATE(id) \
29 id.impulse_handle = id##_handle; \
31 void id##_handle(entity this)
36 * 0 reserved (no input)
43 * 143: emergency teleport
44 * 148: unfairly eliminate
47 * 200 to 209: prev weapon shortcuts
48 * 210 to 219: best weapon shortcuts
49 * 220 to 229: next weapon shortcuts
50 * 230 to 253: individual weapons (up to 24)
53 // weapon switching impulses
55 bool autocvar_g_weaponswitch_debug;
58 IMPULSE(weapon_group_##slot) \
62 this.impulse = IMP_weapon_group_##slot.impulse; \
65 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
67 .entity weaponentity = weaponentities[wepslot]; \
68 W_NextWeaponOnImpulse(this, slot, weaponentity); \
69 if(wepslot == 0 && !autocvar_g_weaponswitch_debug) \
85 // custom order weapon cycling
87 #define X(slot, dir) \
88 IMPULSE(weapon_priority_##slot##_##dir) \
90 if (this.vehicle) return; \
93 this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
96 noref int prev = -1; \
98 noref int next = +1; \
99 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
101 .entity weaponentity = weaponentities[wepslot]; \
102 W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir, weaponentity); \
103 if(wepslot == 0 && !autocvar_g_weaponswitch_debug) \
144 IMPULSE(weapon_byid_##i) \
146 if (this.vehicle) return; \
149 this.impulse = IMP_weapon_byid_##i.impulse; \
152 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \
154 .entity weaponentity = weaponentities[slot]; \
155 W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i), weaponentity); \
156 if(slot == 0 && !autocvar_g_weaponswitch_debug) \
186 IMPULSE(weapon_next_byid)
188 if (this.vehicle) return;
191 this.impulse = IMP_weapon_next_byid.impulse;
194 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
196 .entity weaponentity = weaponentities[slot];
197 W_NextWeapon(this, 0, weaponentity);
199 if(slot == 0 && !autocvar_g_weaponswitch_debug)
204 IMPULSE(weapon_prev_byid)
206 if (this.vehicle) return;
209 this.impulse = IMP_weapon_prev_byid.impulse;
212 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
214 .entity weaponentity = weaponentities[slot];
215 W_PreviousWeapon(this, 0, weaponentity);
217 if(slot == 0 && !autocvar_g_weaponswitch_debug)
222 IMPULSE(weapon_next_bygroup)
224 if (this.vehicle) return;
227 this.impulse = IMP_weapon_next_bygroup.impulse;
230 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
232 .entity weaponentity = weaponentities[slot];
233 W_NextWeapon(this, 1, weaponentity);
235 if(slot == 0 && !autocvar_g_weaponswitch_debug)
240 IMPULSE(weapon_prev_bygroup)
242 if (this.vehicle) return;
245 this.impulse = IMP_weapon_prev_bygroup.impulse;
248 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
250 .entity weaponentity = weaponentities[slot];
251 W_PreviousWeapon(this, 1, weaponentity);
253 if(slot == 0 && !autocvar_g_weaponswitch_debug)
258 IMPULSE(weapon_next_bypriority)
260 if (this.vehicle) return;
263 this.impulse = IMP_weapon_next_bypriority.impulse;
266 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
268 .entity weaponentity = weaponentities[slot];
269 W_NextWeapon(this, 2, weaponentity);
271 if(slot == 0 && !autocvar_g_weaponswitch_debug)
276 IMPULSE(weapon_prev_bypriority)
278 if (this.vehicle) return;
281 this.impulse = IMP_weapon_prev_bypriority.impulse;
284 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
286 .entity weaponentity = weaponentities[slot];
287 W_PreviousWeapon(this, 2, weaponentity);
289 if(slot == 0 && !autocvar_g_weaponswitch_debug)
296 if (this.vehicle) return;
297 if (IS_DEAD(this)) return;
298 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
300 .entity weaponentity = weaponentities[slot];
301 W_LastWeapon(this, weaponentity);
303 if(slot == 0 && !autocvar_g_weaponswitch_debug)
310 if (this.vehicle) return;
311 if (IS_DEAD(this)) return;
312 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
314 .entity weaponentity = weaponentities[slot];
315 W_SwitchWeapon(this, w_getbestweapon(this, weaponentity), weaponentity);
317 if(slot == 0 && !autocvar_g_weaponswitch_debug)
324 if (this.vehicle) return;
325 if (IS_DEAD(this)) return;
326 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
328 .entity weaponentity = weaponentities[slot];
329 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
331 if(slot == 0 && !autocvar_g_weaponswitch_debug)
336 IMPULSE(weapon_reload)
338 if (this.vehicle) return;
339 if (IS_DEAD(this)) return;
340 if (forbidWeaponUse(this)) return;
342 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
344 .entity weaponentity = weaponentities[slot];
345 Weapon w = this.(weaponentity).m_weapon;
346 w.wr_reload(w, actor, weaponentity);
348 if(slot == 0 && !autocvar_g_weaponswitch_debug)
353 void ImpulseCommands(entity this)
355 if (gameover) return;
357 int imp = this.impulse;
361 if (MinigameImpulse(this, imp)) return;
363 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
365 // allow only weapon change impulses when not in round time
366 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
368 #define X(id) case IMP_##id.impulse:
383 X(weapon_next_bygroup)
384 X(weapon_prev_bygroup)
385 X(weapon_next_bypriority)
386 X(weapon_prev_bypriority)
390 X(weapon_priority_0_prev)
391 X(weapon_priority_1_prev)
392 X(weapon_priority_2_prev)
393 X(weapon_priority_3_prev)
394 X(weapon_priority_4_prev)
395 X(weapon_priority_5_prev)
396 X(weapon_priority_6_prev)
397 X(weapon_priority_7_prev)
398 X(weapon_priority_8_prev)
399 X(weapon_priority_9_prev)
400 X(weapon_priority_0_next)
401 X(weapon_priority_1_next)
402 X(weapon_priority_2_next)
403 X(weapon_priority_3_next)
404 X(weapon_priority_4_next)
405 X(weapon_priority_5_next)
406 X(weapon_priority_6_next)
407 X(weapon_priority_7_next)
408 X(weapon_priority_8_next)
409 X(weapon_priority_9_next)
410 X(weapon_priority_0_best)
411 X(weapon_priority_1_best)
412 X(weapon_priority_2_best)
413 X(weapon_priority_3_best)
414 X(weapon_priority_4_best)
415 X(weapon_priority_5_best)
416 X(weapon_priority_6_best)
417 X(weapon_priority_7_best)
418 X(weapon_priority_8_best)
419 X(weapon_priority_9_best)
450 if (vehicle_impulse(this, imp)) return;
452 if (CheatImpulse(this, imp)) return;
454 FOREACH(IMPULSES, it.impulse == imp, {
455 void(entity) f = it.impulse_handle;
467 IMPULSE(waypoint_personal_here)
469 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
470 if (wp) WaypointSprite_Ping(wp);
471 sprint(this, "personal waypoint spawned at location\n");
474 IMPULSE(waypoint_personal_crosshair)
476 WarpZone_crosshair_trace(this);
477 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
478 if (wp) WaypointSprite_Ping(wp);
479 sprint(this, "personal waypoint spawned at crosshair\n");
482 IMPULSE(waypoint_personal_death)
484 if (!this.death_origin) return;
485 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
486 if (wp) WaypointSprite_Ping(wp);
487 sprint(this, "personal waypoint spawned at death location\n");
490 IMPULSE(waypoint_here_follow)
492 if (!teamplay) return;
493 if (IS_DEAD(this)) return;
494 if (!MUTATOR_CALLHOOK(HelpMePing, this))
496 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
497 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
498 else WaypointSprite_Ping(wp);
500 sprint(this, "HELP ME attached\n");
503 IMPULSE(waypoint_here_here)
505 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
506 if (wp) WaypointSprite_Ping(wp);
507 sprint(this, "HERE spawned at location\n");
510 IMPULSE(waypoint_here_crosshair)
512 WarpZone_crosshair_trace(this);
513 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
514 if (wp) WaypointSprite_Ping(wp);
515 sprint(this, "HERE spawned at crosshair\n");
518 IMPULSE(waypoint_here_death)
520 if (!this.death_origin) return;
521 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
522 if (wp) WaypointSprite_Ping(wp);
523 sprint(this, "HERE spawned at death location\n");
526 IMPULSE(waypoint_danger_here)
528 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
529 if (wp) WaypointSprite_Ping(wp);
530 sprint(this, "DANGER spawned at location\n");
533 IMPULSE(waypoint_danger_crosshair)
535 WarpZone_crosshair_trace(this);
536 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
537 if (wp) WaypointSprite_Ping(wp);
538 sprint(this, "DANGER spawned at crosshair\n");
541 IMPULSE(waypoint_danger_death)
543 if (!this.death_origin) return;
544 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
545 if (wp) WaypointSprite_Ping(wp);
546 sprint(this, "DANGER spawned at death location\n");
549 IMPULSE(waypoint_clear_personal)
551 WaypointSprite_ClearPersonal(this);
554 delete(this.personal);
555 this.personal = NULL;
557 sprint(this, "personal waypoint cleared\n");
560 IMPULSE(waypoint_clear)
562 WaypointSprite_ClearOwned(this);
565 delete(this.personal);
566 this.personal = NULL;
568 sprint(this, "all waypoints cleared\n");
571 IMPULSE(navwaypoint_spawn)
573 if (!autocvar_g_waypointeditor) return;
574 waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
575 bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
578 IMPULSE(navwaypoint_remove)
580 if (!autocvar_g_waypointeditor) return;
581 entity e = navigation_findnearestwaypoint(this, false);
583 if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
584 bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
588 IMPULSE(navwaypoint_relink)
590 if (!autocvar_g_waypointeditor) return;
591 waypoint_schedulerelinkall();
594 IMPULSE(navwaypoint_save)
596 if (!autocvar_g_waypointeditor) return;
600 IMPULSE(navwaypoint_unreachable)
602 if (!autocvar_g_waypointeditor) return;
603 IL_EACH(g_waypoints, true,
605 it.colormod = '0.5 0.5 0.5';
606 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
608 entity e2 = navigation_findnearestwaypoint(this, false);
609 navigation_markroutes(this, e2);
615 IL_EACH(g_waypoints, it.wpcost >= 10000000,
617 LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
619 it.effects |= EF_NODEPTHTEST | EF_BLUE;
623 if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
624 navigation_markroutes_inverted(e2);
627 IL_EACH(g_waypoints, it.wpcost >= 10000000,
629 LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
631 if (!(it.effects & EF_NODEPTHTEST)) // not already reported before
633 it.effects |= EF_NODEPTHTEST | EF_RED;
636 if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
637 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
640 IL_EACH(g_spawnpoints, true,
642 vector org = it.origin;
643 tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
644 setorigin(it, trace_endpos);
645 if (navigation_findnearestwaypoint(it, false))
648 it.effects &= ~EF_NODEPTHTEST;
654 LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
655 it.effects |= EF_NODEPTHTEST;
656 _setmodel(it, this.model);
657 it.frame = this.frame;
659 it.colormod = '8 0.5 8';
660 setsize(it, '0 0 0', '0 0 0');
664 if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
667 IL_EACH(g_items, true,
669 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
670 it.colormod = '0.5 0.5 0.5';
672 IL_EACH(g_items, true,
674 if (navigation_findnearestwaypoint(it, false)) continue;
675 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
676 it.effects |= EF_NODEPTHTEST | EF_RED;
680 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
683 IL_EACH(g_items, true,
685 if (navigation_findnearestwaypoint(it, true)) continue;
686 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
687 it.effects |= EF_NODEPTHTEST | EF_BLUE;
691 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);