1 #include "round_handler.qh"
3 #include "bot/waypoints.qh"
5 #include "weapons/throwing.qh"
6 #include "command/common.qh"
8 #include "bot/navigation.qh"
9 #include "weapons/selection.qh"
10 #include "weapons/tracing.qh"
11 #include "weapons/weaponsystem.qh"
13 #include "../common/minigames/sv_minigames.qh"
15 #include "../common/weapons/all.qh"
16 #include "../common/vehicles/sv_vehicles.qh"
18 #include "../common/mutators/mutator/waypoints/waypointsprites.qh"
22 #define IMPULSE(id) _IMPULSE(IMP_##id)
23 #define _IMPULSE(id) \
24 void id##_handle(entity this); \
25 STATIC_INIT_LATE(id) \
27 id.impulse_handle = id##_handle; \
29 void id##_handle(entity this)
34 * 0 reserved (no input)
41 * 143: emergency teleport
42 * 148: unfairly eliminate
45 * 200 to 209: prev weapon shortcuts
46 * 210 to 219: best weapon shortcuts
47 * 220 to 229: next weapon shortcuts
48 * 230 to 253: individual weapons (up to 24)
51 // weapon switching impulses
54 IMPULSE(weapon_group_##slot) \
58 this.impulse = IMP_weapon_group_##slot.impulse; \
61 LOG_INFO("PlayerPostThink impulse setting at time ", ftos(time), "\n"); \
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 LOG_INFO("Setting weapon by cycle\n"); \
91 W_CycleWeapon(this.cvar_cl_weaponpriorities[slot], dir); \
130 IMPULSE(weapon_byid_##i) \
132 if (this.vehicle) return; \
135 this.impulse = IMP_weapon_byid_##i.impulse; \
138 LOG_INFO("Setting weapon by ID\n"); \
139 W_SwitchWeapon(Weapons_from(WEP_FIRST + i)); \
167 IMPULSE(weapon_next_byid)
169 if (this.vehicle) return;
172 this.impulse = IMP_weapon_next_byid.impulse;
175 LOG_INFO("Setting next by ID\n");
179 IMPULSE(weapon_prev_byid)
181 if (this.vehicle) return;
184 this.impulse = IMP_weapon_prev_byid.impulse;
187 LOG_INFO("Setting prev by ID\n");
191 IMPULSE(weapon_next_bygroup)
193 if (this.vehicle) return;
196 this.impulse = IMP_weapon_next_bygroup.impulse;
199 LOG_INFO("Setting next by group\n");
203 IMPULSE(weapon_prev_bygroup)
205 if (this.vehicle) return;
208 this.impulse = IMP_weapon_prev_bygroup.impulse;
211 LOG_INFO("Setting prev by group\n");
215 IMPULSE(weapon_next_bypriority)
217 if (this.vehicle) return;
220 this.impulse = IMP_weapon_next_bypriority.impulse;
223 LOG_INFO("Setting next by priority\n");
227 IMPULSE(weapon_prev_bypriority)
229 if (this.vehicle) return;
232 this.impulse = IMP_weapon_prev_bypriority.impulse;
235 LOG_INFO("Setting prev by priority\n");
241 if (this.vehicle) return;
242 if (IS_DEAD(this)) return;
248 if (this.vehicle) return;
249 if (IS_DEAD(this)) return;
250 W_SwitchWeapon(w_getbestweapon(this));
255 if (this.vehicle) return;
256 if (IS_DEAD(this)) return;
257 W_ThrowWeapon(W_CalculateProjectileVelocity(this.velocity, v_forward * 750, false), '0 0 0', true);
260 IMPULSE(weapon_reload)
262 if (this.vehicle) return;
263 if (IS_DEAD(this)) return;
264 if (forbidWeaponUse(this)) return;
265 Weapon w = PS(this).m_weapon;
267 .entity weaponentity = weaponentities[0]; // TODO: unhardcode
268 w.wr_reload(w, actor, weaponentity);
271 void ImpulseCommands(entity this)
273 if (gameover) return;
275 int imp = this.impulse;
279 if (MinigameImpulse(this, imp)) return;
281 if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused
283 // allow only weapon change impulses when not in round time
284 if (round_handler_IsActive() && !round_handler_IsRoundStarted())
286 #define X(id) case IMP_##id.impulse:
301 X(weapon_next_bygroup)
302 X(weapon_prev_bygroup)
303 X(weapon_next_bypriority)
304 X(weapon_prev_bypriority)
308 X(weapon_priority_0_prev)
309 X(weapon_priority_1_prev)
310 X(weapon_priority_2_prev)
311 X(weapon_priority_3_prev)
312 X(weapon_priority_4_prev)
313 X(weapon_priority_5_prev)
314 X(weapon_priority_6_prev)
315 X(weapon_priority_7_prev)
316 X(weapon_priority_8_prev)
317 X(weapon_priority_9_prev)
318 X(weapon_priority_0_next)
319 X(weapon_priority_1_next)
320 X(weapon_priority_2_next)
321 X(weapon_priority_3_next)
322 X(weapon_priority_4_next)
323 X(weapon_priority_5_next)
324 X(weapon_priority_6_next)
325 X(weapon_priority_7_next)
326 X(weapon_priority_8_next)
327 X(weapon_priority_9_next)
328 X(weapon_priority_0_best)
329 X(weapon_priority_1_best)
330 X(weapon_priority_2_best)
331 X(weapon_priority_3_best)
332 X(weapon_priority_4_best)
333 X(weapon_priority_5_best)
334 X(weapon_priority_6_best)
335 X(weapon_priority_7_best)
336 X(weapon_priority_8_best)
337 X(weapon_priority_9_best)
368 if (vehicle_impulse(this, imp)) return;
370 if (CheatImpulse(imp)) return;
372 FOREACH(IMPULSES, it.impulse == imp, {
373 void(entity) f = it.impulse_handle;
385 IMPULSE(waypoint_personal_here)
387 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.origin, RADARICON_WAYPOINT);
388 if (wp) WaypointSprite_Ping(wp);
389 sprint(this, "personal waypoint spawned at location\n");
392 IMPULSE(waypoint_personal_crosshair)
394 WarpZone_crosshair_trace(this);
395 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, trace_endpos, RADARICON_WAYPOINT);
396 if (wp) WaypointSprite_Ping(wp);
397 sprint(this, "personal waypoint spawned at crosshair\n");
400 IMPULSE(waypoint_personal_death)
402 if (!this.death_origin) return;
403 entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this.death_origin, RADARICON_WAYPOINT);
404 if (wp) WaypointSprite_Ping(wp);
405 sprint(this, "personal waypoint spawned at death location\n");
408 IMPULSE(waypoint_here_follow)
410 if (!teamplay) return;
411 if (IS_DEAD(this)) return;
412 if (!MUTATOR_CALLHOOK(HelpMePing, this))
414 entity wp = WaypointSprite_Attach(WP_Helpme, true, RADARICON_HELPME);
415 if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier);
416 else WaypointSprite_Ping(wp);
418 sprint(this, "HELP ME attached\n");
421 IMPULSE(waypoint_here_here)
423 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.origin, RADARICON_HERE);
424 if (wp) WaypointSprite_Ping(wp);
425 sprint(this, "HERE spawned at location\n");
428 IMPULSE(waypoint_here_crosshair)
430 WarpZone_crosshair_trace(this);
431 entity wp = WaypointSprite_DeployFixed(WP_Here, false, trace_endpos, RADARICON_HERE);
432 if (wp) WaypointSprite_Ping(wp);
433 sprint(this, "HERE spawned at crosshair\n");
436 IMPULSE(waypoint_here_death)
438 if (!this.death_origin) return;
439 entity wp = WaypointSprite_DeployFixed(WP_Here, false, this.death_origin, RADARICON_HERE);
440 if (wp) WaypointSprite_Ping(wp);
441 sprint(this, "HERE spawned at death location\n");
444 IMPULSE(waypoint_danger_here)
446 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.origin, RADARICON_DANGER);
447 if (wp) WaypointSprite_Ping(wp);
448 sprint(this, "DANGER spawned at location\n");
451 IMPULSE(waypoint_danger_crosshair)
453 WarpZone_crosshair_trace(this);
454 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, trace_endpos, RADARICON_DANGER);
455 if (wp) WaypointSprite_Ping(wp);
456 sprint(this, "DANGER spawned at crosshair\n");
459 IMPULSE(waypoint_danger_death)
461 if (!this.death_origin) return;
462 entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this.death_origin, RADARICON_DANGER);
463 if (wp) WaypointSprite_Ping(wp);
464 sprint(this, "DANGER spawned at death location\n");
467 IMPULSE(waypoint_clear_personal)
469 WaypointSprite_ClearPersonal();
472 remove(this.personal);
473 this.personal = NULL;
475 sprint(this, "personal waypoint cleared\n");
478 IMPULSE(waypoint_clear)
480 WaypointSprite_ClearOwned();
483 remove(this.personal);
484 this.personal = NULL;
486 sprint(this, "all waypoints cleared\n");
489 IMPULSE(navwaypoint_spawn)
491 if (!autocvar_g_waypointeditor) return;
492 waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0));
493 bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n"));
496 IMPULSE(navwaypoint_remove)
498 if (!autocvar_g_waypointeditor) return;
499 entity e = navigation_findnearestwaypoint(this, false);
501 if (e.wpflags & WAYPOINTFLAG_GENERATED) return;
502 bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n"));
506 IMPULSE(navwaypoint_relink)
508 if (!autocvar_g_waypointeditor) return;
509 waypoint_schedulerelinkall();
512 IMPULSE(navwaypoint_save)
514 if (!autocvar_g_waypointeditor) return;
518 IMPULSE(navwaypoint_unreachable)
520 if (!autocvar_g_waypointeditor) return;
521 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
523 e.colormod = '0.5 0.5 0.5';
524 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
526 entity e2 = navigation_findnearestwaypoint(this, false);
527 navigation_markroutes(e2);
533 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
535 if (e.wpcost < 10000000) continue;
536 LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n");
538 e.effects |= EF_NODEPTHTEST | EF_BLUE;
542 if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i);
543 navigation_markroutes_inverted(e2);
546 for (entity e = findchain(classname, "waypoint"); e; e = e.chain)
548 if (e.wpcost < 10000000) continue;
549 LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n");
551 if (!(e.effects & EF_NODEPTHTEST)) // not already reported before
553 e.effects |= EF_NODEPTHTEST | EF_RED;
556 if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i);
557 if (m) LOG_INFOF("%d waypoints have been marked total\n", m);
560 for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain)
562 vector org = e.origin;
563 tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, world);
564 setorigin(e, trace_endpos);
565 if (navigation_findnearestwaypoint(e, false))
568 e.effects &= ~EF_NODEPTHTEST;
574 LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
575 e.effects |= EF_NODEPTHTEST;
576 _setmodel(e, this.model);
577 e.frame = this.frame;
579 e.colormod = '8 0.5 8';
580 setsize(e, '0 0 0', '0 0 0');
584 if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i);
587 entity start = findchainflags(flags, FL_ITEM);
588 for (entity e = start; e; e = e.chain)
590 e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE);
591 e.colormod = '0.5 0.5 0.5';
593 for (entity e = start; e; e = e.chain)
595 if (navigation_findnearestwaypoint(e, false)) continue;
596 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
597 e.effects |= EF_NODEPTHTEST | EF_RED;
601 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i);
604 for (entity e = start; e; e = e.chain)
606 if (navigation_findnearestwaypoint(e, true)) continue;
607 LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n");
608 e.effects |= EF_NODEPTHTEST | EF_BLUE;
612 if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i);