1#include "cl_cmd.qh" 2// ============================================== 3// CSQC client commands code, written by Samual 4// Last updated: December 28th, 2011 5// ============================================== 6 7#include <common/command/_mod.qh> 8#include "cl_cmd.qh" 9 10#include "../autocvars.qh" 11#include "../defs.qh" 12#include <client/hud/_mod.qh> 13#include "../main.qh" 14#include "../mapvoting.qh" 15#include "../miscfunctions.qh" 16 17#include "../mutators/events.qh" 18 19#include <common/mapinfo.qh> 20 21void DrawDebugModel(entity this) 22{ 23 if (time - floor(time) > 0.5) 24 { 25 PolyDrawModel(this); 26 this.drawmask = 0; 27 } 28 else 29 { 30 this.renderflags = 0; 31 this.drawmask = MASK_NORMAL; 32 } 33} 34 35 36// ======================= 37// Command Sub-Functions 38// ======================= 39 40void LocalCommand_blurtest(int request) 41{ 42 TC(int, request); 43 // Simple command to work with postprocessing temporarily... possibly completely pointless, the glsl shader is used for a real feature now... 44 // Anyway, to enable it, just compile the client with -DBLURTEST and then you can use the command. 45 46 #ifdef BLURTEST 47 switch (request) 48 { 49 case CMD_REQUEST_COMMAND: 50 { 51 blurtest_time0 = time; 52 blurtest_time1 = time + stof(argv(1)); 53 blurtest_radius = stof(argv(2)); 54 blurtest_power = stof(argv(3)); 55 LOG_INFO("Enabled blurtest\n"); 56 return; 57 } 58 59 default: 60 case CMD_REQUEST_USAGE: 61 { 62 LOG_INFO("\nUsage:^3 cl_cmd blurtest\n"); 63 LOG_INFO(" No arguments required.\n"); 64 return; 65 } 66 } 67 #else 68 if (request) 69 { 70 LOG_INFO("Blurtest is not enabled on this client.\n"); 71 return; 72 } 73 #endif 74} 75 76void LocalCommand_boxparticles(int request, int argc) 77{ 78 TC(int, request); TC(int, argc); 79 switch (request) 80 { 81 case CMD_REQUEST_COMMAND: 82 { 83 if (argc == 9) 84 { 85 int effect = _particleeffectnum(argv(1)); 86 if (effect >= 0) 87 { 88 int index = stoi(argv(2)); 89 entity own; 90 if (index <= 0) 91 own = entitybyindex(-index); 92 else 93 own = findfloat(NULL, entnum, index); 94 vector org_from = stov(argv(3)); 95 vector org_to = stov(argv(4)); 96 vector dir_from = stov(argv(5)); 97 vector dir_to = stov(argv(6)); 98 int countmultiplier = stoi(argv(7)); 99 int flags = stoi(argv(8)); 100 boxparticles(effect, own, org_from, org_to, dir_from, dir_to, countmultiplier, flags); 101 return; 102 } 103 } 104 } 105 106 default: 107 { 108 LOG_INFO("Incorrect parameters for ^2boxparticles^7\n"); 109 } 110 case CMD_REQUEST_USAGE: 111 { 112 LOG_INFO("\nUsage:^3 lv_cmd boxparticles effectname own org_from org_to, dir_from, dir_to, countmultiplier, flags\n"); 113 LOG_INFO(" 'effectname' is the name of a particle effect in effectinfo.txt\n"); 114 LOG_INFO(" 'own' is the entity number of the owner (negative for csqc ent, positive for svqc ent)\n"); 115 LOG_INFO(" 'org_from' is the starting origin of the box\n"); 116 LOG_INFO(" 'org_to' is the ending origin of the box\n"); 117 LOG_INFO(" 'dir_from' is the minimum velocity\n"); 118 LOG_INFO(" 'dir_to' is the maximum velocity\n"); 119 LOG_INFO(" 'countmultiplier' defines a multiplier for the particle count (affects count only, not countabsolute or trailspacing)\n"); 120 LOG_INFO(" 'flags' can contain:\n"); 121 LOG_INFO(" 1 to respect globals particles_alphamin, particles_alphamax (set right before via prvm_globalset client)\n"); 122 LOG_INFO(" 2 to respect globals particles_colormin, particles_colormax (set right before via prvm_globalset client)\n"); 123 LOG_INFO(" 4 to respect globals particles_fade (set right before via prvm_globalset client)\n"); 124 LOG_INFO(" 128 to draw a trail, not a box\n"); 125 return; 126 } 127 } 128} 129 130void LocalCommand_create_scrshot_ent(int request) 131{ 132 TC(int, request); 133 switch (request) 134 { 135 case CMD_REQUEST_COMMAND: 136 { 137 string path = ((argv(1) == "") ? "" : strcat(argv(1), "/")); 138 string filename = strcat(path, MapInfo_Map_bspname, "_scrshot_ent.txt"); 139 int fh = fopen(filename, FILE_APPEND); 140 141 if (fh >= 0) 142 { 143 fputs(fh, "{\n"); 144 fputs(fh, strcat("\"classname\" \"info_autoscreenshot\"\n")); 145 fputs(fh, strcat("\"origin\" \"", strcat(ftos(view_origin.x), " ", ftos(view_origin.y), " ", ftos(view_origin.z)), "\"\n")); 146 fputs(fh, strcat("\"angles\" \"", strcat(ftos(view_angles.x), " ", ftos(view_angles.y), " ", ftos(view_angles.z)), "\"\n")); 147 fputs(fh, "}\n"); 148 149 LOG_INFO("Completed screenshot entity dump in ^2data/data/", path, MapInfo_Map_bspname, "_scrshot_ent.txt^7.\n"); 150 151 fclose(fh); 152 } 153 else 154 { 155 LOG_INFO("^1Error: ^7Could not dump to file!\n"); 156 } 157 return; 158 } 159 160 default: 161 case CMD_REQUEST_USAGE: 162 { 163 LOG_INFO("\nUsage:^3 cl_cmd create_scrshot_ent [path]\n"); 164 LOG_INFO(" Where 'path' can be the subdirectory of data/data in which the file is saved.\n"); 165 return; 166 } 167 } 168} 169 170void LocalCommand_debugmodel(int request, int argc) 171{ 172 TC(int, request); TC(int, argc); 173 switch (request) 174 { 175 case CMD_REQUEST_COMMAND: 176 { 177 string modelname = argv(1); 178 179 entity debugmodel_entity = new(debugmodel); 180 precache_model(modelname); 181 _setmodel(debugmodel_entity, modelname); 182 setorigin(debugmodel_entity, view_origin); 183 debugmodel_entity.angles = view_angles; 184 debugmodel_entity.draw = DrawDebugModel; 185 IL_PUSH(g_drawables, debugmodel_entity); 186 187 return; 188 } 189 190 default: 191 case CMD_REQUEST_USAGE: 192 { 193 LOG_INFO("\nUsage:^3 cl_cmd debugmodel model\n"); 194 LOG_INFO(" Where 'model' is a string of the model name to use for the debug model.\n"); 195 return; 196 } 197 } 198} 199 200void LocalCommand_handlevote(int request, int argc) 201{ 202 TC(int, request); TC(int, argc); 203 switch (request) 204 { 205 case CMD_REQUEST_COMMAND: 206 { 207 int vote_selection; 208 string vote_string; 209 210 if (InterpretBoolean(argv(1))) 211 { 212 vote_selection = 2; 213 vote_string = "yes"; 214 } 215 else 216 { 217 vote_selection = 1; 218 vote_string = "no"; 219 } 220 221 if (vote_selection) 222 { 223 if (uid2name_dialog) // handled by "uid2name" option 224 { 225 vote_active = 0; 226 vote_prev = 0; 227 vote_change = -9999; 228 localcmd(strcat("setreport cl_allow_uid2name ", ftos(vote_selection - 1), "\n")); 229 uid2name_dialog = 0; 230 } 231 else { localcmd(strcat("cmd vote ", vote_string, "\n")); } 232 233 return; 234 } 235 } 236 237 default: 238 { 239 LOG_INFO("Incorrect parameters for ^2handlevote^7\n"); 240 } 241 case CMD_REQUEST_USAGE: 242 { 243 LOG_INFO("\nUsage:^3 cl_cmd handlevote vote\n"); 244 LOG_INFO(" Where 'vote' is the selection for either the current poll or uid2name.\n"); 245 return; 246 } 247 } 248} 249 250bool QuickMenu_IsOpened(); 251void QuickMenu_Close(); 252bool QuickMenu_Open(string mode, string submenu, string file); 253 254bool HUD_MinigameMenu_IsOpened(); 255void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger); 256void HUD_MinigameMenu_Open(); 257 258void HUD_Radar_Show_Maximized(bool doshow, bool clickable); 259 260void LocalCommand_hud(int request, int argc) 261{ 262 TC(int, request); TC(int, argc); 263 switch (request) 264 { 265 case CMD_REQUEST_COMMAND: 266 { 267 if(MUTATOR_CALLHOOK(HUD_Command, argc)) 268 return; 269 270 switch (argv(1)) 271 { 272 case "configure": 273 { 274 cvar_set("_hud_configure", ftos(!autocvar__hud_configure)); 275 return; 276 } 277 278 case "quickmenu": 279 { 280 if (argv(2) == "help") 281 { 282 LOG_INFO(" quickmenu [[default | file | \"\"] submenu file]\n"); 283 LOG_INFO("Called without options (or with \"\") loads either the default quickmenu or a quickmenu file if hud_panel_quickmenu_file is set to a valid filename.\n"); 284 LOG_INFO("A submenu name can be given to open the quickmenu directly in a submenu; it requires to specify 'default', 'file' or '\"\"' option.\n"); 285 LOG_INFO("A file name can also be given to open a different quickmenu\n"); 286 return; 287 } 288 string file = ((argv(4) == "") ? autocvar_hud_panel_quickmenu_file : argv(4)); 289 if (QuickMenu_IsOpened()) 290 QuickMenu_Close(); 291 else 292 QuickMenu_Open(argv(2), argv(3), file); // mode, submenu 293 return; 294 } 295 296 case "save": 297 { 298 if (argv(2)) 299 { 300 HUD_Panel_ExportCfg(argv(2)); 301 return; 302 } 303 else 304 { 305 break; // go to usage, we're missing the paramater needed here. 306 } 307 } 308 309 case "scoreboard_columns_set": 310 { 311 Cmd_Scoreboard_SetFields(argc); 312 return; 313 } 314 315 case "scoreboard_columns_help": 316 { 317 Cmd_Scoreboard_Help(); 318 return; 319 } 320 321 case "radar": 322 { 323 if (argv(2)) 324 HUD_Radar_Show_Maximized(InterpretBoolean(argv(2)), 0); 325 else 326 HUD_Radar_Show_Maximized(!hud_panel_radar_maximized, 0); 327 return; 328 } 329 330 case "clickradar": 331 { 332 HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1); 333 return; 334 } 335 } 336 } 337 338 default: 339 { 340 LOG_INFO("Incorrect parameters for ^2hud^7\n"); 341 } 342 case CMD_REQUEST_USAGE: 343 { 344 LOG_INFO("\nUsage:^3 cl_cmd hud action [configname | radartoggle | layout]\n"); 345 LOG_INFO(" Where 'action' is the command to complete,\n"); 346 LOG_INFO(" 'configname' is the name to save to for \"save\" action,\n"); 347 LOG_INFO(" 'radartoggle' is to control hud_panel_radar_maximized for \"radar\" action,\n"); 348 LOG_INFO(" and 'layout' is how to organize the scoreboard columns for the set action.\n"); 349 LOG_INFO(" Full list of commands here: \"configure, quickmenu, minigame, save, scoreboard_columns_help, scoreboard_columns_set, radar.\"\n"); 350 return; 351 } 352 } 353} 354 355void LocalCommand_localprint(int request, int argc) 356{ 357 TC(int, request); TC(int, argc); 358 switch (request) 359 { 360 case CMD_REQUEST_COMMAND: 361 { 362 if (argv(1)) 363 { 364 centerprint_hud(argv(1)); 365 return; 366 } 367 } 368 369 default: 370 { 371 LOG_INFO("Incorrect parameters for ^2localprint^7\n"); 372 } 373 case CMD_REQUEST_USAGE: 374 { 375 LOG_INFO("\nUsage:^3 cl_cmd localprint \"message\"\n"); 376 LOG_INFO(" 'message' is the centerprint message to send to yourself.\n"); 377 return; 378 } 379 } 380} 381 382void LocalCommand_mv_download(int request, int argc) 383{ 384 TC(int, request); TC(int, argc); 385 switch (request) 386 { 387 case CMD_REQUEST_COMMAND: 388 { 389 if (argv(1)) 390 { 391 Cmd_MapVote_MapDownload(argc); 392 return; 393 } 394 } 395 396 default: 397 { 398 LOG_INFO("Incorrect parameters for ^2mv_download^7\n"); 399 } 400 case CMD_REQUEST_USAGE: 401 { 402 LOG_INFO("\nUsage:^3 cl_cmd mv_download mapid\n"); 403 LOG_INFO(" Where 'mapid' is the id number of the map to request an image of on the map vote selection menu.\n"); 404 return; 405 } 406 } 407} 408 409void LocalCommand_sendcvar(int request, int argc) 410{ 411 TC(int, request); TC(int, argc); 412 switch (request) 413 { 414 case CMD_REQUEST_COMMAND: 415 { 416 if (argv(1)) 417 { 418 // W_FixWeaponOrder will trash argv, so save what we need. 419 string thiscvar = strzone(argv(1)); 420 string s = cvar_string(thiscvar); 421 422 if (thiscvar == "cl_weaponpriority") 423 s = W_FixWeaponOrder(W_NumberWeaponOrder(s), 1); 424 else if (substring(thiscvar, 0, 17) == "cl_weaponpriority" && strlen(thiscvar) == 18) 425 s = W_FixWeaponOrder(W_NumberWeaponOrder(s), 0); 426 427 localcmd("cmd sentcvar ", thiscvar, " \"", s, "\"\n"); 428 strunzone(thiscvar); 429 return; 430 } 431 } 432 433 default: 434 { 435 LOG_INFO("Incorrect parameters for ^2sendcvar^7\n"); 436 } 437 case CMD_REQUEST_USAGE: 438 { 439 LOG_INFO("\nUsage:^3 cl_cmd sendcvar <cvar>\n"); 440 LOG_INFO(" Where 'cvar' is the cvar plus arguments to send to the server.\n"); 441 return; 442 } 443 } 444} 445 446/* use this when creating a new command, making sure to place it in alphabetical order... also, 447** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! 448void LocalCommand_(int request) 449{ 450 switch(request) 451 { 452 case CMD_REQUEST_COMMAND: 453 { 454 455 return; 456 } 457 458 default: 459 case CMD_REQUEST_USAGE: 460 { 461 print("\nUsage:^3 cl_cmd \n"); 462 print(" No arguments required.\n"); 463 return; 464 } 465 } 466} 467*/ 468 469 470// ================================== 471// Macro system for client commands 472// ================================== 473 474// Normally do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) 475CLIENT_COMMAND(blurtest, "Feature for testing blur postprocessing") { LocalCommand_blurtest(request); } 476CLIENT_COMMAND(boxparticles, "Spawn particles manually") { LocalCommand_boxparticles(request, arguments); } 477CLIENT_COMMAND(create_scrshot_ent, "Create an entity at this location for automatic screenshots") { LocalCommand_create_scrshot_ent(request); } 478CLIENT_COMMAND(debugmodel, "Spawn a debug model manually") { LocalCommand_debugmodel(request, arguments); } 479CLIENT_COMMAND(handlevote, "System to handle selecting a vote or option") { LocalCommand_handlevote(request, arguments); } 480CLIENT_COMMAND(hud, "Commands regarding/controlling the HUD system") { LocalCommand_hud(request, arguments); } 481CLIENT_COMMAND(localprint, "Create your own centerprint sent to yourself") { LocalCommand_localprint(request, arguments); } 482CLIENT_COMMAND(mv_download, "Retrieve mapshot picture from the server") { LocalCommand_mv_download(request, arguments); } 483CLIENT_COMMAND(sendcvar, "Send a cvar to the server (like weaponpriority)") { LocalCommand_sendcvar(request, arguments); } 484 485void LocalCommand_macro_help() 486{ 487 FOREACH(CLIENT_COMMANDS, true, LOG_INFOF(" ^2%s^7: %s\n", it.m_name, it.m_description)); 488} 489 490bool LocalCommand_macro_command(int argc, string command) 491{ 492 string c = strtolower(argv(0)); 493 FOREACH(CLIENT_COMMANDS, it.m_name == c, { 494 it.m_invokecmd(it, CMD_REQUEST_COMMAND, NULL, argc, command); 495 return true; 496 }); 497 return false; 498} 499 500bool LocalCommand_macro_usage(int argc) 501{ 502 string c = strtolower(argv(1)); 503 FOREACH(CLIENT_COMMANDS, it.m_name == c, { 504 it.m_invokecmd(it, CMD_REQUEST_USAGE, NULL, argc, ""); 505 return true; 506 }); 507 return false; 508} 509 510void LocalCommand_macro_write_aliases(int fh) 511{ 512 FOREACH(CLIENT_COMMANDS, true, CMD_Write_Alias("qc_cmd_cl", it.m_name, it.m_description)); 513} 514 515 516// ========================================= 517// Main Function Called By Engine (cl_cmd) 518// ========================================= 519// If this function exists, client code handles gamecommand instead of the engine code. 520 521void GameCommand(string command) 522{ 523 int argc = tokenize_console(command); 524 525 // Guide for working with argc arguments by example: 526 // argc: 1 - 2 - 3 - 4 527 // argv: 0 - 1 - 2 - 3 528 // cmd vote - master - login - password 529 string s = strtolower(argv(0)); 530 if (s == "help") 531 { 532 if (argc == 1) 533 { 534 LOG_INFO("\nClient console commands:\n"); 535 LocalCommand_macro_help(); 536 537 LOG_INFO("\nGeneric commands shared by all programs:\n"); 538 GenericCommand_macro_help(); 539 540 LOG_INFO("\nUsage:^3 cl_cmd COMMAND...^7, where possible commands are listed above.\n"); 541 LOG_INFO("For help about a specific command, type cl_cmd help COMMAND\n"); 542 543 return; 544 } 545 else if (GenericCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it 546 { 547 return; 548 } 549 else if (LocalCommand_macro_usage(argc)) // now try for normal commands too 550 { 551 return; 552 } 553 } 554 // continue as usual and scan for normal commands 555 if (GenericCommand(command) // handled by common/command/generic.qc 556 || LocalCommand_macro_command(argc, command) // handled by one of the above LocalCommand_* functions 557 || MUTATOR_CALLHOOK(CSQC_ConsoleCommand, s, argc, command) // handled by a mutator 558 ) return; 559 560 // nothing above caught the command, must be invalid 561 LOG_INFO(((command != "") ? strcat("Unknown client command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try cl_cmd help.\n"); 562} 563 564 565// =================================== 566// Macro system for console commands 567// =================================== 568 569// These functions are here specifically to add special + - commands to the game, and are not really normal commands. 570// Please add client commands to the function above this, as this is only for special reasons. 571#define CONSOLE_COMMANDS_NORMAL() \ 572 CONSOLE_COMMAND("+showscores", { scoreboard_showscores = true; }) \ 573 CONSOLE_COMMAND("-showscores", { scoreboard_showscores = false; }) \ 574 CONSOLE_COMMAND("+showaccuracy", { scoreboard_showaccuracy = true; }) \ 575 CONSOLE_COMMAND("-showaccuracy", { scoreboard_showaccuracy = false; }) \ 576 /* nothing */ 577 578#define CONSOLE_COMMANDS_MOVEMENT() \ 579 CONSOLE_COMMAND("+forward", { ++camera_direction.x; }) \ 580 CONSOLE_COMMAND("-forward", { --camera_direction.x; }) \ 581 CONSOLE_COMMAND("+back", { --camera_direction.x; }) \ 582 CONSOLE_COMMAND("-back", { ++camera_direction.x; }) \ 583 CONSOLE_COMMAND("+moveup", { ++camera_direction.z; }) \ 584 CONSOLE_COMMAND("-moveup", { --camera_direction.z; }) \ 585 CONSOLE_COMMAND("+movedown", { --camera_direction.z; }) \ 586 CONSOLE_COMMAND("-movedown", { ++camera_direction.z; }) \ 587 CONSOLE_COMMAND("+moveright", { --camera_direction.y; }) \ 588 CONSOLE_COMMAND("-moveright", { ++camera_direction.y; }) \ 589 CONSOLE_COMMAND("+moveleft", { ++camera_direction.y; }) \ 590 CONSOLE_COMMAND("-moveleft", { --camera_direction.y; }) \ 591 CONSOLE_COMMAND("+roll_right", { ++camera_roll; }) \ 592 CONSOLE_COMMAND("-roll_right", { --camera_roll; }) \ 593 CONSOLE_COMMAND("+roll_left", { --camera_roll; }) \ 594 CONSOLE_COMMAND("-roll_left", { ++camera_roll; }) \ 595 /* nothing */ 596 597void ConsoleCommand_macro_init() 598{ 599 // first init normal commands 600 #define CONSOLE_COMMAND(name, execution) \ 601 { registercommand(name); } 602 603 CONSOLE_COMMANDS_NORMAL(); 604 #undef CONSOLE_COMMAND 605 606 // then init movement commands 607 #ifndef CAMERATEST 608 if (isdemo()) 609 { 610 #endif 611 #define CONSOLE_COMMAND(name, execution) \ 612 registercommand(name); 613 614 CONSOLE_COMMANDS_MOVEMENT(); 615 #undef CONSOLE_COMMAND 616 #ifndef CAMERATEST 617} 618 #endif 619} 620 621bool ConsoleCommand_macro_normal(string s, int argc) 622{ 623 #define CONSOLE_COMMAND(name, execution) \ 624 { if (name == s) { { execution } return true; } } 625 626 CONSOLE_COMMANDS_NORMAL(); 627 #undef CONSOLE_COMMAND 628 629 return false; 630} 631 632bool ConsoleCommand_macro_movement(string s, int argc) 633{ 634 if (camera_active) 635 { 636 #define CONSOLE_COMMAND(name, execution) \ 637 { if (name == s) { { execution } return true; } } 638 639 CONSOLE_COMMANDS_MOVEMENT(); 640 #undef CONSOLE_COMMAND 641 } 642 643 return false; 644} 645 646 647// ====================================================== 648// Main Function Called By Engine (registered commands) 649// ====================================================== 650// Used to parse commands in the console that have been registered with the "registercommand" function 651 652bool CSQC_ConsoleCommand(string command) 653{ 654 int argc = tokenize_console(command); 655 string s = strtolower(argv(0)); 656 // Return value should be true if CSQC handled the command, otherwise return false to have the engine handle it. 657 return ConsoleCommand_macro_normal(s, argc) 658 || ConsoleCommand_macro_movement(s, argc) 659 ; 660} 661