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.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.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, 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.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, 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.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, 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.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.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, 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.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();
466 remove(this.personal);
467 this.personal = NULL;
469 sprint(this, "personal waypoint cleared\n");
472 IMPULSE(waypoint_clear)
474 WaypointSprite_ClearOwned();
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 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
517 e.colormod = '0.5 0.5 0.5';
518 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
520 entity e2 = navigation_findnearestwaypoint(this, false);
521 navigation_markroutes(this, e2);
527 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
529 if (e.wpcost < 10000000) continue;
530 LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
532 e.effects |= EF_NODEPTHTEST | EF_BLUE;
536 if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
537 navigation_markroutes_inverted(e2);
540 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
542 if (e.wpcost < 10000000) continue;
543 LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
545 if (!(e.effects & EF_NODEPTHTEST)) // not already reported before
547 e.effects |= EF_NODEPTHTEST | EF_RED;
550 if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
551 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
554 for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
556 vector org = e.origin;
557 tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, world);
558 setorigin(e, trace_endpos);
559 if (navigation_findnearestwaypoint(e, false))
562 e.effects &= ~EF_NODEPTHTEST;
568 LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
569 e.effects |= EF_NODEPTHTEST;
570 _setmodel(e, this.model);
571 e.frame = this.frame;
573 e.colormod = '8 0.5 8';
574 setsize(e, '0 0 0', '0 0 0');
578 if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
581 entity start = findchainflags(flags, FL_ITEM);
582 for (entity e = start; e; e = e.chain)
584 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
585 e.colormod = '0.5 0.5 0.5';
587 for (entity e = start; e; e = e.chain)
589 if (navigation_findnearestwaypoint(e, false)) continue;
590 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
591 e.effects |= EF_NODEPTHTEST | EF_RED;
595 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
598 for (entity e = start; e; e = e.chain)
600 if (navigation_findnearestwaypoint(e, true)) continue;
601 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
602 e.effects |= EF_NODEPTHTEST | EF_BLUE;
606 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);