1 #include "cl_impulse.qh"
2 #include "round_handler.qh"
4 #include "bot/waypoints.qh"
6 #include "weapons/throwing.qh"
7 #include "command/common.qh"
9 #include "bot/navigation.qh"
10 #include "weapons/selection.qh"
11 #include "weapons/tracing.qh"
12 #include "weapons/weaponsystem.qh"
14 #include <common/state.qh>
16 #include "../common/minigames/sv_minigames.qh"
18 #include "../common/weapons/all.qh"
19 #include "../common/vehicles/sv_vehicles.qh"
21 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
25 #define IMPULSE(id) _IMPULSE(IMP_##id)
26 #define _IMPULSE(id) \
27 void id##_handle(entity this); \
28 STATIC_INIT_LATE(id) \
30 id.impulse_handle = id##_handle; \
32 void id##_handle(entity this)
37 * 0 reserved (no input)
44 * 143: emergency teleport
45 * 148: unfairly eliminate
48 * 200 to 209: prev weapon shortcuts
49 * 210 to 219: best weapon shortcuts
50 * 220 to 229: next weapon shortcuts
51 * 230 to 253: individual weapons (up to 24)
54 // weapon switching impulses
57 IMPULSE(weapon_group_##slot) \
61 this.impulse = IMP_weapon_group_##slot.impulse; \
64 W_NextWeaponOnImpulse(this, slot); \
78 // custom order weapon cycling
80 #define X(slot, dir) \
81 IMPULSE(weapon_priority_##slot##_##dir) \
83 if (this.vehicle) return; \
86 this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
89 noref int prev = -1; \
91 noref int next = +1; \
92 W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \
131 IMPULSE(weapon_byid_##i) \
133 if (this.vehicle) return; \
136 this.impulse = IMP_weapon_byid_##i.impulse; \
139 W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \
167 IMPULSE(weapon_next_byid)
169 if (this.vehicle) return;
172 this.impulse = IMP_weapon_next_byid.impulse;
175 W_NextWeapon(this, 0);
178 IMPULSE(weapon_prev_byid)
180 if (this.vehicle) return;
183 this.impulse = IMP_weapon_prev_byid.impulse;
186 W_PreviousWeapon(this, 0);
189 IMPULSE(weapon_next_bygroup)
191 if (this.vehicle) return;
194 this.impulse = IMP_weapon_next_bygroup.impulse;
197 W_NextWeapon(this, 1);
200 IMPULSE(weapon_prev_bygroup)
202 if (this.vehicle) return;
205 this.impulse = IMP_weapon_prev_bygroup.impulse;
208 W_PreviousWeapon(this, 1);
211 IMPULSE(weapon_next_bypriority)
213 if (this.vehicle) return;
216 this.impulse = IMP_weapon_next_bypriority.impulse;
219 W_NextWeapon(this, 2);
222 IMPULSE(weapon_prev_bypriority)
224 if (this.vehicle) return;
227 this.impulse = IMP_weapon_prev_bypriority.impulse;
230 W_PreviousWeapon(this, 2);
235 if (this.vehicle) return;
236 if (IS_DEAD(this)) return;
242 if (this.vehicle) return;
243 if (IS_DEAD(this)) return;
244 W_SwitchWeapon(this, w_getbestweapon(this));
249 if (this.vehicle) return;
250 if (IS_DEAD(this)) return;
251 W_ThrowWeapon(this, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true);
254 IMPULSE(weapon_reload)
256 if (this.vehicle) return;
257 if (IS_DEAD(this)) return;
258 if (forbidWeaponUse(this)) return;
259 Weapon w = PS(this).m_weapon;
261 .entity weaponentity = weaponentities[0]; // TODO: unhardcode
262 w.wr_reload(w, actor, weaponentity);
265 void ImpulseCommands(entity this)
267 if (gameover) return;
269 int imp = this.impulse;
273 if (MinigameImpulse(this, imp)) return;
275 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
277 // allow only weapon change impulses when not in round time
278 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
280 #define X(id) case IMP_##id.impulse:
295 X(weapon_next_bygroup)
296 X(weapon_prev_bygroup)
297 X(weapon_next_bypriority)
298 X(weapon_prev_bypriority)
302 X(weapon_priority_0_prev)
303 X(weapon_priority_1_prev)
304 X(weapon_priority_2_prev)
305 X(weapon_priority_3_prev)
306 X(weapon_priority_4_prev)
307 X(weapon_priority_5_prev)
308 X(weapon_priority_6_prev)
309 X(weapon_priority_7_prev)
310 X(weapon_priority_8_prev)
311 X(weapon_priority_9_prev)
312 X(weapon_priority_0_next)
313 X(weapon_priority_1_next)
314 X(weapon_priority_2_next)
315 X(weapon_priority_3_next)
316 X(weapon_priority_4_next)
317 X(weapon_priority_5_next)
318 X(weapon_priority_6_next)
319 X(weapon_priority_7_next)
320 X(weapon_priority_8_next)
321 X(weapon_priority_9_next)
322 X(weapon_priority_0_best)
323 X(weapon_priority_1_best)
324 X(weapon_priority_2_best)
325 X(weapon_priority_3_best)
326 X(weapon_priority_4_best)
327 X(weapon_priority_5_best)
328 X(weapon_priority_6_best)
329 X(weapon_priority_7_best)
330 X(weapon_priority_8_best)
331 X(weapon_priority_9_best)
362 if (vehicle_impulse(this, imp)) return;
364 if (CheatImpulse(this, imp)) return;
366 FOREACH(IMPULSES, it.impulse == imp, {
367 void(entity) f = it.impulse_handle;
379 IMPULSE(waypoint_personal_here)
381 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT);
382 if (wp) WaypointSprite_Ping(wp);
383 sprint(this, "personal waypoint spawned at location\n");
386 IMPULSE(waypoint_personal_crosshair)
388 WarpZone_crosshair_trace(this);
389 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT);
390 if (wp) WaypointSprite_Ping(wp);
391 sprint(this, "personal waypoint spawned at crosshair\n");
394 IMPULSE(waypoint_personal_death)
396 if (!this.death_origin) return;
397 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT);
398 if (wp) WaypointSprite_Ping(wp);
399 sprint(this, "personal waypoint spawned at death location\n");
402 IMPULSE(waypoint_here_follow)
404 if (!teamplay) return;
405 if (IS_DEAD(this)) return;
406 if (!MUTATOR_CALLHOOK(HelpMePing, this))
408 entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME);
409 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
410 else WaypointSprite_Ping(wp);
412 sprint(this, "HELP ME attached\n");
415 IMPULSE(waypoint_here_here)
417 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE);
418 if (wp) WaypointSprite_Ping(wp);
419 sprint(this, "HERE spawned at location\n");
422 IMPULSE(waypoint_here_crosshair)
424 WarpZone_crosshair_trace(this);
425 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE);
426 if (wp) WaypointSprite_Ping(wp);
427 sprint(this, "HERE spawned at crosshair\n");
430 IMPULSE(waypoint_here_death)
432 if (!this.death_origin) return;
433 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE);
434 if (wp) WaypointSprite_Ping(wp);
435 sprint(this, "HERE spawned at death location\n");
438 IMPULSE(waypoint_danger_here)
440 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER);
441 if (wp) WaypointSprite_Ping(wp);
442 sprint(this, "DANGER spawned at location\n");
445 IMPULSE(waypoint_danger_crosshair)
447 WarpZone_crosshair_trace(this);
448 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER);
449 if (wp) WaypointSprite_Ping(wp);
450 sprint(this, "DANGER spawned at crosshair\n");
453 IMPULSE(waypoint_danger_death)
455 if (!this.death_origin) return;
456 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER);
457 if (wp) WaypointSprite_Ping(wp);
458 sprint(this, "DANGER spawned at death location\n");
461 IMPULSE(waypoint_clear_personal)
463 WaypointSprite_ClearPersonal(this);
466 remove(this.personal);
467 this.personal = NULL;
469 sprint(this, "personal waypoint cleared\n");
472 IMPULSE(waypoint_clear)
474 WaypointSprite_ClearOwned(this);
477 remove(this.personal);
478 this.personal = NULL;
480 sprint(this, "all waypoints cleared\n");
483 IMPULSE(navwaypoint_spawn)
485 if (!autocvar_g_waypointeditor) return;
486 waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
487 bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
490 IMPULSE(navwaypoint_remove)
492 if (!autocvar_g_waypointeditor) return;
493 entity e = navigation_findnearestwaypoint(this, false);
495 if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
496 bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
500 IMPULSE(navwaypoint_relink)
502 if (!autocvar_g_waypointeditor) return;
503 waypoint_schedulerelinkall();
506 IMPULSE(navwaypoint_save)
508 if (!autocvar_g_waypointeditor) return;
512 IMPULSE(navwaypoint_unreachable)
514 if (!autocvar_g_waypointeditor) return;
515 FOREACH_ENTITY_CLASS("waypoint", true,
517 it.colormod = '0.5 0.5 0.5';
518 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
520 entity e2 = navigation_findnearestwaypoint(this, false);
521 navigation_markroutes(this, e2);
527 FOREACH_ENTITY_CLASS("waypoint", it.wpcost >= 10000000,
529 LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n");
531 it.effects |= EF_NODEPTHTEST | EF_BLUE;
535 if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j);
536 navigation_markroutes_inverted(e2);
539 FOREACH_ENTITY_CLASS("waypoint", it.wpcost >= 10000000,
541 LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n");
543 if (!(it.effects & EF_NODEPTHTEST)) // not already reported before
545 it.effects |= EF_NODEPTHTEST | EF_RED;
548 if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j);
549 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
552 FOREACH_ENTITY_CLASS("info_player_deathmatch", true,
554 vector org = it.origin;
555 tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL);
556 setorigin(it, trace_endpos);
557 if (navigation_findnearestwaypoint(it, false))
560 it.effects &= ~EF_NODEPTHTEST;
566 LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
567 it.effects |= EF_NODEPTHTEST;
568 _setmodel(it, this.model);
569 it.frame = this.frame;
571 it.colormod = '8 0.5 8';
572 setsize(it, '0 0 0', '0 0 0');
576 if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j);
579 FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
581 it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
582 it.colormod = '0.5 0.5 0.5';
584 FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
586 if (navigation_findnearestwaypoint(it, false)) continue;
587 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
588 it.effects |= EF_NODEPTHTEST | EF_RED;
592 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j);
595 FOREACH_ENTITY_FLAGS(flags, FL_ITEM,
597 if (navigation_findnearestwaypoint(it, true)) continue;
598 LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n");
599 it.effects |= EF_NODEPTHTEST | EF_BLUE;
603 if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j);