+void GameCommand_trace(float request, float argc)
+{
+ entity e;
+ vector org, delta, start, end, p, q, q0, pos, vv, dv;
+ float i, f, safe, unsafe, dq, dqf;
+
+ // TODO: Clean up all of these variables and merge the code below to use only a few
+
+ switch(request)
+ {
+ case GC_REQUEST_HELP:
+ print(" ^2trace^7: Various debugging tools with tracing\n");
+ return;
+
+ case GC_REQUEST_COMMAND:
+ switch(argv(1))
+ {
+ case "debug":
+ print("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.\n");
+ for(;;)
+ {
+ org = world.mins;
+ delta = world.maxs - world.mins;
+
+ start_x = org_x + random() * delta_x;
+ start_y = org_y + random() * delta_y;
+ start_z = org_z + random() * delta_z;
+
+ end_x = org_x + random() * delta_x;
+ end_y = org_y + random() * delta_y;
+ end_z = org_z + random() * delta_z;
+
+ start = stov(vtos(start));
+ end = stov(vtos(end));
+
+ tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
+ if(!trace_startsolid)
+ {
+ p = trace_endpos;
+ tracebox(p, PL_MIN, PL_MAX, p, MOVE_NOMONSTERS, world);
+ if(trace_startsolid || trace_fraction == 1)
+ {
+ rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
+ tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
+ tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
+
+ if(trace_startsolid)
+ {
+ // how much do we need to back off?
+ safe = 1;
+ unsafe = 0;
+ for(;;)
+ {
+ pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
+ tracebox(pos, PL_MIN, PL_MAX, pos, MOVE_NOMONSTERS, world);
+ if(trace_startsolid)
+ {
+ if((safe + unsafe) * 0.5 == unsafe)
+ break;
+ unsafe = (safe + unsafe) * 0.5;
+ }
+ else
+ {
+ if((safe + unsafe) * 0.5 == safe)
+ break;
+ safe = (safe + unsafe) * 0.5;
+ }
+ }
+
+ print("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
+ print("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
+
+ tracebox(p, PL_MIN + '0.1 0.1 0.1', PL_MAX - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, world);
+ if(trace_startsolid)
+ print("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
+ else
+ print("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
+ break;
+ }
+
+ q0 = p;
+ dq = 0;
+ dqf = 1;
+ for(;;)
+ {
+ q = p + normalize(end - p) * (dq + dqf);
+ if(q == q0)
+ break;
+ tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
+ if(trace_startsolid)
+ error("THIS ONE cannot happen");
+ if(trace_fraction > 0)
+ dq += dqf * trace_fraction;
+ dqf *= 0.5;
+ q0 = q;
+ }
+ if(dq > 0)
+ {
+ print("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
+ print("could go ", ftos(dq), " units further to ", vtos(q), "\n");
+ break;
+ }
+ }
+ }
+ }
+ return;
+
+ case "debug2":
+ e = nextent(world);
+ tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e);
+ vv = trace_endpos;
+ if(trace_fraction == 1)
+ {
+ print("not above ground, aborting\n");
+ return;
+ }
+ f = 0;
+ for(i = 0; i < 100000; ++i)
+ {
+ dv = randomvec();
+ if(dv_z > 0)
+ dv = -1 * dv;
+ tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e);
+ if(trace_startsolid)
+ print("bug 1\n");
+ if(trace_fraction == 1)
+ if(dv_z < f)
+ {
+ print("bug 2: ", ftos(dv_x), " ", ftos(dv_y), " ", ftos(dv_z));
+ print(" (", ftos(asin(dv_z / vlen(dv)) * 180 / M_PI), " degrees)\n");
+ f = dv_z;
+ }
+ }
+ print("highest possible dist: ", ftos(f), "\n");
+ return;
+
+ case "walk":
+ if(argc == 3)
+ {
+ e = nextent(world);
+ if(tracewalk(e, stov(argv(1)), e.mins, e.maxs, stov(argv(2)), MOVE_NORMAL))
+ print("can walk\n");
+ else
+ print("cannot walk\n");
+ return;
+ }
+
+ case "showline":
+ if(argc == 3)
+ {
+ vv = stov(argv(1));
+ dv = stov(argv(2));
+ traceline(vv, dv, MOVE_NORMAL, world);
+ trailparticles(world, particleeffectnum("TR_NEXUIZPLASMA"), vv, trace_endpos);
+ trailparticles(world, particleeffectnum("TR_CRYLINKPLASMA"), trace_endpos, dv);
+ return;
+ }
+
+ // no default case, just go straight to "invalid arguments"
+ }
+
+ default:
+ case GC_REQUEST_USAGE:
+ print("\nUsage:^3 sv_cmd trace command [arguments]\n");
+ print(" FIXME: Arguments currently unknown\n");
+ return;
+ }
+}
+
+void GameCommand_unlockteams(float request)
+{
+ switch(request)
+ {
+ case GC_REQUEST_HELP:
+ print(" ^2unlockteams^7: Enable the ability for players to switch or enter teams\n");
+ return;
+
+ case GC_REQUEST_COMMAND:
+ if(teamplay)
+ {
+ lockteams = 0;
+ bprint("^1The teams are now unlocked.\n");
+ }
+ else
+ bprint("That command can only be used in a team-based gamemode.\n");
+ return;
+
+ default:
+ case GC_REQUEST_USAGE:
+ print("\nUsage:^3 sv_cmd unlockteams\n");
+ print(" No arguments required.\n");
+ print("See also: ^2lockteams^7\n");
+ return;
+ }
+}
+
+void GameCommand_warp(float request, float argc)
+{
+ switch (request)
+ {
+ case GC_REQUEST_HELP:
+ print(" ^2warp^7: Choose different level in campaign\n");
+ return;
+
+ case GC_REQUEST_COMMAND:
+ if(autocvar_g_campaign)
+ {
+
+ if(argc >= 2)
+ {
+ CampaignLevelWarp(stof(argv(1)));
+ print("Successfully warped to campaign level ", stof(argv(1)), ".\n");
+ }
+ else
+ {
+ CampaignLevelWarp(-1);
+ print("Successfully warped to next campaign level.\n");
+ }
+ }
+ else
+ print("Not in campaign, can't level warp\n");
+ return;
+
+ default:
+ case GC_REQUEST_USAGE:
+ print("\nUsage:^3 sv_cmd level\n");
+ print(" 'level' is the level to change campaign mode to.\n");
+ return;
+ }
+}
+
+
+// =========================================
+// Main Function Called By Engine (sv_cmd)
+// =========================================
+// If this function exists, game code handles gamecommand instead of the engine code.
+