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/minigames/sv_minigames.qh"
16 #include "../common/weapons/all.qh"
17 #include "../common/vehicles/sv_vehicles.qh"
19 #include "../common/mutators/mutator/waypoints/waypointsprites.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
55 IMPULSE(weapon_group_##slot) \
59 this.impulse = IMP_weapon_group_##slot.impulse; \
62 W_NextWeaponOnImpulse(slot); \
76 // custom order weapon cycling
78 #define X(slot, dir) \
79 IMPULSE(weapon_priority_##slot##_##dir) \
81 if (this.vehicle) return; \
84 this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \
87 noref int prev = -1; \
89 noref int next = +1; \
90 W_CycleWeapon(this.cvar_cl_weaponpriorities[slot], dir); \
129 IMPULSE(weapon_byid_##i) \
131 if (this.vehicle) return; \
134 this.impulse = IMP_weapon_byid_##i.impulse; \
137 W_SwitchWeapon(Weapons_from(WEP_FIRST + i)); \
165 IMPULSE(weapon_next_byid)
167 if (this.vehicle) return;
170 this.impulse = IMP_weapon_next_byid.impulse;
176 IMPULSE(weapon_prev_byid)
178 if (this.vehicle) return;
181 this.impulse = IMP_weapon_prev_byid.impulse;
187 IMPULSE(weapon_next_bygroup)
189 if (this.vehicle) return;
192 this.impulse = IMP_weapon_next_bygroup.impulse;
198 IMPULSE(weapon_prev_bygroup)
200 if (this.vehicle) return;
203 this.impulse = IMP_weapon_prev_bygroup.impulse;
209 IMPULSE(weapon_next_bypriority)
211 if (this.vehicle) return;
214 this.impulse = IMP_weapon_next_bypriority.impulse;
220 IMPULSE(weapon_prev_bypriority)
222 if (this.vehicle) return;
225 this.impulse = IMP_weapon_prev_bypriority.impulse;
233 if (this.vehicle) return;
234 if (IS_DEAD(this)) return;
240 if (this.vehicle) return;
241 if (IS_DEAD(this)) return;
242 W_SwitchWeapon(w_getbestweapon(this));
247 if (this.vehicle) return;
248 if (IS_DEAD(this)) return;
249 W_ThrowWeapon(W_CalculateProjectileVelocity(this.velocity, v_forward * 750, false), '0 0 0', true);
252 IMPULSE(weapon_reload)
254 if (this.vehicle) return;
255 if (IS_DEAD(this)) return;
256 if (forbidWeaponUse(this)) return;
257 Weapon w = PS(this).m_weapon;
259 .entity weaponentity = weaponentities[0]; // TODO: unhardcode
260 w.wr_reload(w, actor, weaponentity);
263 void ImpulseCommands(entity this)
265 if (gameover) return;
267 int imp = this.impulse;
271 if (MinigameImpulse(this, imp)) return;
273 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
275 // allow only weapon change impulses when not in round time
276 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
278 #define X(id) case IMP_##id.impulse:
293 X(weapon_next_bygroup)
294 X(weapon_prev_bygroup)
295 X(weapon_next_bypriority)
296 X(weapon_prev_bypriority)
300 X(weapon_priority_0_prev)
301 X(weapon_priority_1_prev)
302 X(weapon_priority_2_prev)
303 X(weapon_priority_3_prev)
304 X(weapon_priority_4_prev)
305 X(weapon_priority_5_prev)
306 X(weapon_priority_6_prev)
307 X(weapon_priority_7_prev)
308 X(weapon_priority_8_prev)
309 X(weapon_priority_9_prev)
310 X(weapon_priority_0_next)
311 X(weapon_priority_1_next)
312 X(weapon_priority_2_next)
313 X(weapon_priority_3_next)
314 X(weapon_priority_4_next)
315 X(weapon_priority_5_next)
316 X(weapon_priority_6_next)
317 X(weapon_priority_7_next)
318 X(weapon_priority_8_next)
319 X(weapon_priority_9_next)
320 X(weapon_priority_0_best)
321 X(weapon_priority_1_best)
322 X(weapon_priority_2_best)
323 X(weapon_priority_3_best)
324 X(weapon_priority_4_best)
325 X(weapon_priority_5_best)
326 X(weapon_priority_6_best)
327 X(weapon_priority_7_best)
328 X(weapon_priority_8_best)
329 X(weapon_priority_9_best)
360 if (vehicle_impulse(this, imp)) return;
362 if (CheatImpulse(imp)) return;
364 FOREACH(IMPULSES, it.impulse == imp, {
365 void(entity) f = it.impulse_handle;
377 IMPULSE(waypoint_personal_here)
379 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.origin, RADARICON_WAYPOINT);
380 if (wp) WaypointSprite_Ping(wp);
381 sprint(this, "personal waypoint spawned at location\n");
384 IMPULSE(waypoint_personal_crosshair)
386 WarpZone_crosshair_trace(this);
387 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, trace_endpos, RADARICON_WAYPOINT);
388 if (wp) WaypointSprite_Ping(wp);
389 sprint(this, "personal waypoint spawned at crosshair\n");
392 IMPULSE(waypoint_personal_death)
394 if (!this.death_origin) return;
395 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.death_origin, RADARICON_WAYPOINT);
396 if (wp) WaypointSprite_Ping(wp);
397 sprint(this, "personal waypoint spawned at death location\n");
400 IMPULSE(waypoint_here_follow)
402 if (!teamplay) return;
403 if (IS_DEAD(this)) return;
404 if (!MUTATOR_CALLHOOK(HelpMePing, this))
406 entity wp = WaypointSprite_Attach(WP_Helpme, true, RADARICON_HELPME);
407 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
408 else WaypointSprite_Ping(wp);
410 sprint(this, "HELP ME attached\n");
413 IMPULSE(waypoint_here_here)
415 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.origin, RADARICON_HERE);
416 if (wp) WaypointSprite_Ping(wp);
417 sprint(this, "HERE spawned at location\n");
420 IMPULSE(waypoint_here_crosshair)
422 WarpZone_crosshair_trace(this);
423 entity wp = WaypointSprite_DeployFixed(WP_Here, false, trace_endpos, RADARICON_HERE);
424 if (wp) WaypointSprite_Ping(wp);
425 sprint(this, "HERE spawned at crosshair\n");
428 IMPULSE(waypoint_here_death)
430 if (!this.death_origin) return;
431 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.death_origin, RADARICON_HERE);
432 if (wp) WaypointSprite_Ping(wp);
433 sprint(this, "HERE spawned at death location\n");
436 IMPULSE(waypoint_danger_here)
438 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.origin, RADARICON_DANGER);
439 if (wp) WaypointSprite_Ping(wp);
440 sprint(this, "DANGER spawned at location\n");
443 IMPULSE(waypoint_danger_crosshair)
445 WarpZone_crosshair_trace(this);
446 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, trace_endpos, RADARICON_DANGER);
447 if (wp) WaypointSprite_Ping(wp);
448 sprint(this, "DANGER spawned at crosshair\n");
451 IMPULSE(waypoint_danger_death)
453 if (!this.death_origin) return;
454 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.death_origin, RADARICON_DANGER);
455 if (wp) WaypointSprite_Ping(wp);
456 sprint(this, "DANGER spawned at death location\n");
459 IMPULSE(waypoint_clear_personal)
461 WaypointSprite_ClearPersonal();
464 remove(this.personal);
465 this.personal = NULL;
467 sprint(this, "personal waypoint cleared\n");
470 IMPULSE(waypoint_clear)
472 WaypointSprite_ClearOwned();
475 remove(this.personal);
476 this.personal = NULL;
478 sprint(this, "all waypoints cleared\n");
481 IMPULSE(navwaypoint_spawn)
483 if (!autocvar_g_waypointeditor) return;
484 waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
485 bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
488 IMPULSE(navwaypoint_remove)
490 if (!autocvar_g_waypointeditor) return;
491 entity e = navigation_findnearestwaypoint(this, false);
493 if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
494 bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
498 IMPULSE(navwaypoint_relink)
500 if (!autocvar_g_waypointeditor) return;
501 waypoint_schedulerelinkall();
504 IMPULSE(navwaypoint_save)
506 if (!autocvar_g_waypointeditor) return;
510 IMPULSE(navwaypoint_unreachable)
512 if (!autocvar_g_waypointeditor) return;
513 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
515 e.colormod = '0.5 0.5 0.5';
516 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
518 entity e2 = navigation_findnearestwaypoint(this, false);
519 navigation_markroutes(e2);
525 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
527 if (e.wpcost < 10000000) continue;
528 LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
530 e.effects |= EF_NODEPTHTEST | EF_BLUE;
534 if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
535 navigation_markroutes_inverted(e2);
538 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
540 if (e.wpcost < 10000000) continue;
541 LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
543 if (!(e.effects & EF_NODEPTHTEST)) // not already reported before
545 e.effects |= EF_NODEPTHTEST | EF_RED;
548 if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
549 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
552 for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
554 vector org = e.origin;
555 tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, world);
556 setorigin(e, trace_endpos);
557 if (navigation_findnearestwaypoint(e, false))
560 e.effects &= ~EF_NODEPTHTEST;
566 LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
567 e.effects |= EF_NODEPTHTEST;
568 _setmodel(e, this.model);
569 e.frame = this.frame;
571 e.colormod = '8 0.5 8';
572 setsize(e, '0 0 0', '0 0 0');
576 if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
579 entity start = findchainflags(flags, FL_ITEM);
580 for (entity e = start; e; e = e.chain)
582 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
583 e.colormod = '0.5 0.5 0.5';
585 for (entity e = start; e; e = e.chain)
587 if (navigation_findnearestwaypoint(e, false)) continue;
588 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
589 e.effects |= EF_NODEPTHTEST | EF_RED;
593 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
596 for (entity e = start; e; e = e.chain)
598 if (navigation_findnearestwaypoint(e, true)) continue;
599 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
600 e.effects |= EF_NODEPTHTEST | EF_BLUE;
604 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);