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
56 IMPULSE(weapon_group_##slot) \
60 this.impulse = IMP_weapon_group_##slot.impulse; \
63 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
65 .entity weaponentity = weaponentities[wepslot]; \
66 W_NextWeaponOnImpulse(this, slot, weaponentity); \
81 // custom order weapon cycling
83 #define X(slot, dir) \
84 IMPULSE(weapon_priority_##slot##_##dir) \
86 if (this.vehicle) return; \
89 this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
92 noref int prev = -1; \
94 noref int next = +1; \
95 for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) \
97 .entity weaponentity = weaponentities[wepslot]; \
98 W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir, weaponentity); \
138 IMPULSE(weapon_byid_##i) \
140 if (this.vehicle) return; \
143 this.impulse = IMP_weapon_byid_##i.impulse; \
146 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \
148 .entity weaponentity = weaponentities[slot]; \
149 W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i), weaponentity); \
178 IMPULSE(weapon_next_byid)
180 if (this.vehicle) return;
183 this.impulse = IMP_weapon_next_byid.impulse;
186 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
188 .entity weaponentity = weaponentities[slot];
189 W_NextWeapon(this, 0, weaponentity);
193 IMPULSE(weapon_prev_byid)
195 if (this.vehicle) return;
198 this.impulse = IMP_weapon_prev_byid.impulse;
201 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
203 .entity weaponentity = weaponentities[slot];
204 W_PreviousWeapon(this, 0, weaponentity);
208 IMPULSE(weapon_next_bygroup)
210 if (this.vehicle) return;
213 this.impulse = IMP_weapon_next_bygroup.impulse;
216 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
218 .entity weaponentity = weaponentities[slot];
219 W_NextWeapon(this, 1, weaponentity);
223 IMPULSE(weapon_prev_bygroup)
225 if (this.vehicle) return;
228 this.impulse = IMP_weapon_prev_bygroup.impulse;
231 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
233 .entity weaponentity = weaponentities[slot];
234 W_PreviousWeapon(this, 1, weaponentity);
238 IMPULSE(weapon_next_bypriority)
240 if (this.vehicle) return;
243 this.impulse = IMP_weapon_next_bypriority.impulse;
246 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
248 .entity weaponentity = weaponentities[slot];
249 W_NextWeapon(this, 2, weaponentity);
253 IMPULSE(weapon_prev_bypriority)
255 if (this.vehicle) return;
258 this.impulse = IMP_weapon_prev_bypriority.impulse;
261 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
263 .entity weaponentity = weaponentities[slot];
264 W_PreviousWeapon(this, 2, weaponentity);
270 if (this.vehicle) return;
271 if (IS_DEAD(this)) return;
272 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
274 .entity weaponentity = weaponentities[slot];
275 W_LastWeapon(this, weaponentity);
281 if (this.vehicle) return;
282 if (IS_DEAD(this)) return;
283 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
285 .entity weaponentity = weaponentities[slot];
286 W_SwitchWeapon(this, w_getbestweapon(this, weaponentity), weaponentity);
292 if (this.vehicle) return;
293 if (IS_DEAD(this)) return;
294 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
296 .entity weaponentity = weaponentities[slot];
297 W_ThrowWeapon(this, weaponentity, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
301 IMPULSE(weapon_reload)
303 if (this.vehicle) return;
304 if (IS_DEAD(this)) return;
305 if (forbidWeaponUse(this)) return;
307 for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
309 .entity weaponentity = weaponentities[slot];
310 Weapon w = this.(weaponentity).m_weapon;
311 w.wr_reload(w, actor, weaponentity);
315 void ImpulseCommands(entity this)
317 if (gameover) return;
319 int imp = this.impulse;
323 if (MinigameImpulse(this, imp)) return;
325 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
327 // allow only weapon change impulses when not in round time
328 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
330 #define X(id) case IMP_##id.impulse:
345 X(weapon_next_bygroup)
346 X(weapon_prev_bygroup)
347 X(weapon_next_bypriority)
348 X(weapon_prev_bypriority)
352 X(weapon_priority_0_prev)
353 X(weapon_priority_1_prev)
354 X(weapon_priority_2_prev)
355 X(weapon_priority_3_prev)
356 X(weapon_priority_4_prev)
357 X(weapon_priority_5_prev)
358 X(weapon_priority_6_prev)
359 X(weapon_priority_7_prev)
360 X(weapon_priority_8_prev)
361 X(weapon_priority_9_prev)
362 X(weapon_priority_0_next)
363 X(weapon_priority_1_next)
364 X(weapon_priority_2_next)
365 X(weapon_priority_3_next)
366 X(weapon_priority_4_next)
367 X(weapon_priority_5_next)
368 X(weapon_priority_6_next)
369 X(weapon_priority_7_next)
370 X(weapon_priority_8_next)
371 X(weapon_priority_9_next)
372 X(weapon_priority_0_best)
373 X(weapon_priority_1_best)
374 X(weapon_priority_2_best)
375 X(weapon_priority_3_best)
376 X(weapon_priority_4_best)
377 X(weapon_priority_5_best)
378 X(weapon_priority_6_best)
379 X(weapon_priority_7_best)
380 X(weapon_priority_8_best)
381 X(weapon_priority_9_best)
412 if (vehicle_impulse(this, imp)) return;
414 if (CheatImpulse(this, imp)) return;
416 FOREACH(IMPULSES, it.impulse == imp, {
417 void(entity) f = it.impulse_handle;
429 IMPULSE(waypoint_personal_here)
431 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
432 if (wp) WaypointSprite_Ping(wp);
433 sprint(this, "personal waypoint spawned at location\n");
436 IMPULSE(waypoint_personal_crosshair)
438 WarpZone_crosshair_trace(this);
439 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
440 if (wp) WaypointSprite_Ping(wp);
441 sprint(this, "personal waypoint spawned at crosshair\n");
444 IMPULSE(waypoint_personal_death)
446 if (!this.death_origin) return;
447 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
448 if (wp) WaypointSprite_Ping(wp);
449 sprint(this, "personal waypoint spawned at death location\n");
452 IMPULSE(waypoint_here_follow)
454 if (!teamplay) return;
455 if (IS_DEAD(this)) return;
456 if (!MUTATOR_CALLHOOK(HelpMePing, this))
458 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
459 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
460 else WaypointSprite_Ping(wp);
462 sprint(this, "HELP ME attached\n");
465 IMPULSE(waypoint_here_here)
467 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
468 if (wp) WaypointSprite_Ping(wp);
469 sprint(this, "HERE spawned at location\n");
472 IMPULSE(waypoint_here_crosshair)
474 WarpZone_crosshair_trace(this);
475 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
476 if (wp) WaypointSprite_Ping(wp);
477 sprint(this, "HERE spawned at crosshair\n");
480 IMPULSE(waypoint_here_death)
482 if (!this.death_origin) return;
483 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
484 if (wp) WaypointSprite_Ping(wp);
485 sprint(this, "HERE spawned at death location\n");
488 IMPULSE(waypoint_danger_here)
490 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
491 if (wp) WaypointSprite_Ping(wp);
492 sprint(this, "DANGER spawned at location\n");
495 IMPULSE(waypoint_danger_crosshair)
497 WarpZone_crosshair_trace(this);
498 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
499 if (wp) WaypointSprite_Ping(wp);
500 sprint(this, "DANGER spawned at crosshair\n");
503 IMPULSE(waypoint_danger_death)
505 if (!this.death_origin) return;
506 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
507 if (wp) WaypointSprite_Ping(wp);
508 sprint(this, "DANGER spawned at death location\n");
511 IMPULSE(waypoint_clear_personal)
513 WaypointSprite_ClearPersonal(this);
516 delete(this.personal);
517 this.personal = NULL;
519 sprint(this, "personal waypoint cleared\n");
522 IMPULSE(waypoint_clear)
524 WaypointSprite_ClearOwned(this);
527 delete(this.personal);
528 this.personal = NULL;
530 sprint(this, "all waypoints cleared\n");
533 IMPULSE(navwaypoint_spawn)
535 if (!autocvar_g_waypointeditor) return;
536 waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
537 bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
540 IMPULSE(navwaypoint_remove)
542 if (!autocvar_g_waypointeditor) return;
543 entity e = navigation_findnearestwaypoint(this, false);
545 if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
546 bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
550 IMPULSE(navwaypoint_relink)
552 if (!autocvar_g_waypointeditor) return;
553 waypoint_schedulerelinkall();
556 IMPULSE(navwaypoint_save)
558 if (!autocvar_g_waypointeditor) return;
562 IMPULSE(navwaypoint_unreachable)
564 if (!autocvar_g_waypointeditor) return;
565 IL_EACH(g_waypoints, true,
567 it.colormod = '0.5 0.5 0.5';
568 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
570 entity e2 = navigation_findnearestwaypoint(this, false);
571 navigation_markroutes(this, e2);
577 IL_EACH(g_waypoints, it.wpcost >= 10000000,
579 LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
581 it.effects |= EF_NODEPTHTEST | EF_BLUE;
585 if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
586 navigation_markroutes_inverted(e2);
589 IL_EACH(g_waypoints, it.wpcost >= 10000000,
591 LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
593 if (!(it.effects & EF_NODEPTHTEST)) // not already reported before
595 it.effects |= EF_NODEPTHTEST | EF_RED;
598 if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
599 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
602 IL_EACH(g_spawnpoints, true,
604 vector org = it.origin;
605 tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
606 setorigin(it, trace_endpos);
607 if (navigation_findnearestwaypoint(it, false))
610 it.effects &= ~EF_NODEPTHTEST;
616 LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
617 it.effects |= EF_NODEPTHTEST;
618 _setmodel(it, this.model);
619 it.frame = this.frame;
621 it.colormod = '8 0.5 8';
622 setsize(it, '0 0 0', '0 0 0');
626 if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
629 IL_EACH(g_items, true,
631 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
632 it.colormod = '0.5 0.5 0.5';
634 IL_EACH(g_items, true,
636 if (navigation_findnearestwaypoint(it, false)) continue;
637 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
638 it.effects |= EF_NODEPTHTEST | EF_RED;
642 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
645 IL_EACH(g_items, true,
647 if (navigation_findnearestwaypoint(it, true)) continue;
648 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
649 it.effects |= EF_NODEPTHTEST | EF_BLUE;
653 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);