diff --git a/config/setup.cfg b/config/setup.cfg index dda30aa85..85ffb5f83 100644 --- a/config/setup.cfg +++ b/config/setup.cfg @@ -1,38 +1,48 @@ complete exec . complete copyprefab prefab obr -mapcomplete = [ setcomplete $arg1 1; complete $arg1 maps mpz ]; mapcomplete map -start = [ mode $arg2 $arg3; map $arg1 ]; mapcomplete start +mapcomplete = [ setcomplete $arg1 1; complete $arg1 maps mpz ] +mapcomplete map +mapcomplete start + +all_args = [ + local args + args = "" + loop i $numargs [ + args = (concat $args $[arg@(+ $i 1)]) + ] + result $args +] demo = [ stopdemo; start $arg1 0 0 ]; complete demo demos dmo -edit = [ start $arg1 $modeidxediting (+ $mutsbitffa $mutsbitclassic $arg2) ]; mapcomplete edit -deathmatch = [ start $arg1 $modeidxdeathmatch (| 0 $arg2) ]; mapcomplete deathmatch; dm = [ deathmatch $arg1 $arg2 ]; mapcomplete dm -teamdm = [ start $arg1 $modeidxdeathmatch (| 0 $arg2) ]; mapcomplete teamdm; tdm = [ teamdm $arg1 $arg2 ]; mapcomplete tdm -gladiator = [ start $arg1 $modeidxdeathmatch (| $mutsbitgsp1 $arg2) ]; mapcomplete gladiator -ffa = [ start $arg1 $modeidxdeathmatch (| $mutsbitffa $arg2) ]; mapcomplete ffa; fdm = [ ffa $arg1 $arg2 ]; mapcomplete fdm -multidm = [ start $arg1 $modeidxdeathmatch (| $mutsbitmulti $arg2) ]; mapcomplete multidm; mdm = [ multidm $arg1 $arg2 ]; mapcomplete mdm -coop = [ start $arg1 $modeidxdeathmatch (| $mutsbitcoop $arg2) ]; mapcomplete coop; cdm = [ coop $arg1 $arg2 ]; mapcomplete cdm -capture = [ start $arg1 $modeidxcapture (| 0 $arg2) ]; mapcomplete capture; ctf = [ capture $arg1 $arg2 ]; mapcomplete ctf -defend = [ start $arg1 $modeidxdefend (| 0 $arg2) ]; mapcomplete defend; dnc = [ defend $arg1 $arg2 ]; mapcomplete dnc; dac = [defend $arg1 $arg2 ] ; mapcomplete dac -bomber = [ start $arg1 $modeidxbomber (| 0 $arg2) ]; mapcomplete bomber; bb = [ bomber $arg1 $arg2 ]; mapcomplete bbr -race = [ start $arg1 $modeidxrace (| 0 $arg2) ]; mapcomplete race -instagib = [ start $arg1 $modeidxdeathmatch (| $mutsbitinstagib $arg2) ]; mapcomplete instagib; insta = [ instagib $arg1 $arg2 ]; mapcomplete insta -medieval = [ start $arg1 $modeidxdeathmatch (| $mutsbitmedieval $arg2) ]; mapcomplete medieval -kaboom = [ start $arg1 $modeidxdeathmatch (| $mutsbitkaboom $arg2) ]; mapcomplete kaboom -duel = [ start $arg1 $modeidxdeathmatch (+ $mutsbitduel $mutsbitffa $mutsbitbasic $mutsbithard $arg2) ]; mapcomplete duel -survivor = [ start $arg1 $modeidxdeathmatch (| $mutsbitsurvivor $arg2) ]; mapcomplete survivor; lms = [ survivor $arg1 $arg2 ]; mapcomplete lms -classic = [ start $arg1 $modeidxdeathmatch (| $mutsbitclassic $arg2) ]; mapcomplete classic -quickcapture = [ start $arg1 $modeidxcapture (| $mutsbitgsp1 $arg2) ]; mapcomplete quickcapture -defendcapture = [ start $arg1 $modeidxcapture (| $mutsbitgsp2 $arg2) ]; mapcomplete defendcapture -protectcapture = [ start $arg1 $modeidxcapture (| $mutsbitgsp3 $arg2) ]; mapcomplete protectcapture -quickdefend = [ start $arg1 $modeidxdefend (| $mutsbitgsp1 $arg2) ]; mapcomplete quickdefend -kingdefend = [ start $arg1 $modeidxdefend (| $mutsbitgsp2 $arg2) ]; mapcomplete kingdefend; koth = [ kingdefend $arg1 $arg2 ]; mapcomplete koth -holdbomber = [ start $arg1 $modeidxbomber (| $mutsbitgsp1 $arg2) ]; mapcomplete holdbomber -basketbomber = [ start $arg1 $modeidxbomber (| $mutsbitgsp2 $arg2) ]; mapcomplete basketbomber -attackbomber = [ start $arg1 $modeidxbomber (| $mutsbitgsp3 $arg2) ]; mapcomplete attackbomber -trial = [ start $arg1 $modeidxrace (+ $mutsbitgsp1 $mutsbitffa $arg2) ]; mapcomplete trial -endurancerace = [ start $arg1 $modeidxrace (| $mutsbitgsp2 $arg2) ]; mapcomplete endurancerace -gauntletrace = [ start $arg1 $modeidxrace (| $mutsbitgsp3 $arg2) ]; mapcomplete gauntletrace +edit = [ start $arg1 $modeidxediting $mutsbitffa $mutsbitclassic (sublist (@all_args) 1) ]; mapcomplete edit +deathmatch = [ start $arg1 $modeidxdeathmatch (sublist (@all_args) 1) ]; mapcomplete deathmatch; dm = [ deathmatch $arg1 (sublist (@all_args) 1) ]; mapcomplete dm +teamdm = [ start $arg1 $modeidxdeathmatch (sublist (@all_args) 1) ]; mapcomplete teamdm; tdm = [ teamdm $arg1 (sublist (@all_args) 1) ]; mapcomplete tdm +gladiator = [ start $arg1 $modeidxdeathmatch $mutsbitgsp1 (sublist (@all_args) 1) ]; mapcomplete gladiator +ffa = [ start $arg1 $modeidxdeathmatch $mutsbitffa (sublist (@all_args) 1) ]; mapcomplete ffa; fdm = [ ffa $arg1 (sublist (@all_args) 1) ]; mapcomplete fdm +multidm = [ start $arg1 $modeidxdeathmatch $mutsbitmulti (sublist (@all_args) 1) ]; mapcomplete multidm; mdm = [ multidm $arg1 (sublist (@all_args) 1) ]; mapcomplete mdm +coop = [ start $arg1 $modeidxdeathmatch $mutsbitcoop (sublist (@all_args) 1) ]; mapcomplete coop; cdm = [ coop $arg1 (sublist (@all_args) 1) ]; mapcomplete cdm +capture = [ start $arg1 $modeidxcapture (sublist (@all_args) 1) ]; mapcomplete capture; ctf = [ capture $arg1 (sublist (@all_args) 1) ]; mapcomplete ctf +defend = [ start $arg1 $modeidxdefend (sublist (@all_args) 1) ]; mapcomplete defend; dnc = [ defend $arg1 (sublist (@all_args) 1) ]; mapcomplete dnc; dac = [defend $arg1 (sublist (@all_args) 1) ] ; mapcomplete dac +bomber = [ start $arg1 $modeidxbomber (sublist (@all_args) 1) ]; mapcomplete bomber; bb = [ bomber $arg1 (sublist (@all_args) 1) ]; mapcomplete bbr +race = [ start $arg1 $modeidxrace (sublist (@all_args) 1) ]; mapcomplete race +instagib = [ start $arg1 $modeidxdeathmatch $mutsbitinstagib (sublist (@all_args) 1) ]; mapcomplete instagib; insta = [ instagib $arg1 (sublist (@all_args) 1) ]; mapcomplete insta +medieval = [ start $arg1 $modeidxdeathmatch $mutsbitmedieval (sublist (@all_args) 1) ]; mapcomplete medieval +kaboom = [ start $arg1 $modeidxdeathmatch $mutsbitkaboom (sublist (@all_args) 1) ]; mapcomplete kaboom +duel = [ start $arg1 $modeidxdeathmatch $mutsbitduel $mutsbitffa $mutsbitbasic $mutsbithard (sublist (@all_args) 1) ]; mapcomplete duel +survivor = [ start $arg1 $modeidxdeathmatch $mutsbitsurvivor (sublist (@all_args) 1) ]; mapcomplete survivor; lms = [ survivor $arg1 (sublist (@all_args) 1) ]; mapcomplete lms +classic = [ start $arg1 $modeidxdeathmatch $mutsbitclassic (sublist (@all_args) 1) ]; mapcomplete classic +quickcapture = [ start $arg1 $modeidxcapture $mutsbitgsp1 (sublist (@all_args) 1) ]; mapcomplete quickcapture +defendcapture = [ start $arg1 $modeidxcapture $mutsbitgsp2 (sublist (@all_args) 1) ]; mapcomplete defendcapture +protectcapture = [ start $arg1 $modeidxcapture $mutsbitgsp3 (sublist (@all_args) 1) ]; mapcomplete protectcapture +quickdefend = [ start $arg1 $modeidxdefend $mutsbitgsp1 (sublist (@all_args) 1) ]; mapcomplete quickdefend +kingdefend = [ start $arg1 $modeidxdefend $mutsbitgsp2 (sublist (@all_args) 1) ]; mapcomplete kingdefend; koth = [ kingdefend $arg1 (sublist (@all_args) 1) ]; mapcomplete koth +holdbomber = [ start $arg1 $modeidxbomber $mutsbitgsp1 (sublist (@all_args) 1) ]; mapcomplete holdbomber +basketbomber = [ start $arg1 $modeidxbomber $mutsbitgsp2 (sublist (@all_args) 1) ]; mapcomplete basketbomber +attackbomber = [ start $arg1 $modeidxbomber $mutsbitgsp3 (sublist (@all_args) 1) ]; mapcomplete attackbomber +trial = [ start $arg1 $modeidxrace $mutsbitgsp1 $mutsbitffa (sublist (@all_args) 1) ]; mapcomplete trial +endurancerace = [ start $arg1 $modeidxrace $mutsbitgsp2 (sublist (@all_args) 1) ]; mapcomplete endurancerace +gauntletrace = [ start $arg1 $modeidxrace $mutsbitgsp3 (sublist (@all_args) 1) ]; mapcomplete gauntletrace delta_game_0 = [ if (iszooming) [ setzoom $arg1 ] [ weapon -1 $arg1 ] ] delta_spec_0 = [ followdelta $arg1 ] diff --git a/config/usage.cfg b/config/usage.cfg index afcc7f6cb..3e1c56e9c 100644 --- a/config/usage.cfg +++ b/config/usage.cfg @@ -798,7 +798,7 @@ setdesc "map" "requests a map change to a given map, with no change of mode or m //modes setdesc "gamemode" "returns the current game mode; 0 = demo, 1 = edit, 2 = dm, 3 = ctf, 4 = dac, 5 = bb, 6 = race" setdesc "mode" "sets the mode and mutator values for the next map change request;^nmode sets the mode type,^nmuts sets the mutators according to a bitwise sum of mutator values,^nconveniently set using $modeidx* vars and sums of $mutsbit* vars,^nexample: mode $modeidxdeathmatch (+ $mutsbitinstagib $mutsbitmedieval)" "mode muts" -setdesc "start" "requests a map change to a given map with a specific mode and mutators;^ndepending on privileges, this will force or vote for the map change,^nmode sets the mode type,^nmuts sets the mutators according to a bitwise sum of mutator values,^nconveniently set using $modeidx* vars and sums of $mutsbit* vars,^nexample: start bath $modeidxdeathmatch (+ $mutsbitinstagib $mutsbitmedieval)" "map mode muts" +setdesc "start" "requests a map change to a given map with a specific mode and mutators;^ndepending on privileges, this will force or vote for the map change,^nmode sets the mode, can be as name or number,^nmuts sets the mutators, can be either their names, or a bitwise sum of mutator values,^nconveniently set using $modeidx* vars and sums of $mutsbit* vars,^nexample: start castle dm ffa duel^nexample: start bath $modeidxdeathmatch (+ $mutsbitinstagib $mutsbitmedieval)" "map mode muts" setdesc "edit" "starts editing on a random map. specifying a file name will create a new map file on a blank map canvas" "map" setdesc "demo" "starts playback of a given demo" "demo" setdesc "deathmatch" "requests a map change to deathmatch on a given map;^n[muts] optionally adds extra mutators according to bitwise mutator values, conveniently set using a sum of $mutsbit* vars,^nexample: deathmatch bath (+ $mutsbitinstagib $mutsbitmedieval)" "map [muts]" diff --git a/src/game/client.cpp b/src/game/client.cpp index 27c07d1dc..ef8433a00 100644 --- a/src/game/client.cpp +++ b/src/game/client.cpp @@ -1482,6 +1482,142 @@ namespace client } //ICOMMAND(0, sendmap, "", (), sendmap()); + static void startcmd(char *map, char *mode_str, char *muts_str) + { + static const struct {const char *name; int val;} mode_names[] = + { + {"demo", G_DEMO}, + {"edit", G_EDITMODE}, + {"deathmatch", G_DEATHMATCH}, + {"dm", G_DEATHMATCH}, + {"capture", G_CAPTURE}, + {"ctf", G_CAPTURE}, + {"defend", G_DEFEND}, + {"dac", G_DEFEND}, + {"bomber", G_BOMBER}, + {"bb", G_BOMBER}, + {"race", G_RACE}, + }; + static const struct {const char *name; int val; int modes;} mut_names[] = + { + {"multi", 1 << G_M_MULTI, G_ALL}, + {"ffa", 1 << G_M_FFA, G_ALL}, + {"coop", 1 << G_M_COOP, G_ALL}, + {"insta", 1 << G_M_INSTA, G_ALL}, + {"instagib", 1 << G_M_INSTA, G_ALL}, + {"medieval", 1 << G_M_MEDIEVAL, G_ALL}, + {"kaboom", 1 << G_M_KABOOM, G_ALL}, + {"duel", 1 << G_M_DUEL, G_ALL}, + {"survivor", 1 << G_M_SURVIVOR, G_ALL}, + {"classic", 1 << G_M_CLASSIC, G_ALL}, + {"onslaught", 1 << G_M_ONSLAUGHT, G_ALL}, + {"freestyle", 1 << G_M_FREESTYLE, G_ALL}, + {"vampire", 1 << G_M_VAMPIRE, G_ALL}, + {"resize", 1 << G_M_RESIZE, G_ALL}, + {"hard", 1 << G_M_HARD, G_ALL}, + {"basic", 1 << G_M_BASIC, G_ALL}, + {"gladiator", 1 << G_M_GSP1, 1 << G_DEATHMATCH}, + {"oldscool", 1 << G_M_GSP2, 1 << G_DEATHMATCH}, + {"quick", 1 << G_M_GSP1, 1 << G_CAPTURE}, + {"defend", 1 << G_M_GSP2, 1 << G_CAPTURE}, + {"protect", 1 << G_M_GSP3, 1 << G_CAPTURE}, + {"quick", 1 << G_M_GSP1, 1 << G_DEFEND}, + {"king", 1 << G_M_GSP2, 1 << G_DEFEND}, + {"hold", 1 << G_M_GSP1, 1 << G_BOMBER}, + {"basket", 1 << G_M_GSP2, 1 << G_BOMBER}, + {"attack", 1 << G_M_GSP3, 1 << G_BOMBER}, + {"timed", 1 << G_M_GSP1, 1 << G_RACE}, + {"endurance", 1 << G_M_GSP2, 1 << G_RACE}, + {"gauntlet", 1 << G_M_GSP3, 1 << G_RACE}, + }; + + int nextmode = G_DEATHMATCH; + int nextmuts = 0; + + if(mode_str && isnumeric(*mode_str)) + { + nextmode = atoi(mode_str); + } + else if(mode_str && *mode_str) + { + bool found_one = false; + for(auto possible_mode : mode_names) + { + if(strcmp(possible_mode.name, mode_str) == 0) + { + nextmode = possible_mode.val; + found_one = true; + break; + } + } + if(!found_one) + { + conoutft(CON_MESG, "\frgame mode \"%s\" not found", mode_str); + return; + } + } + + if(muts_str && *muts_str) + { + std::vector muts_vec; + char *begin = muts_str; + char *end = NULL; + // Split the muts string on ' ' and '-' into muts_vec. + while(*begin) + { + if(end) + { + if(*end == ' ' || *end == '-' || *end == '\0') + { + muts_vec.emplace_back(begin, end - begin); + begin = end; + end = NULL; + } + else end++; + } + else + { + if(*begin != ' ' && *begin != '-') + { + end = begin; + } + else begin++; + } + } + + for(const std::string &mut : muts_vec) + { + if(isnumeric(*mut.c_str())) + { + nextmuts |= atoi(mut.c_str()); + } + else + { + bool found_one = false; + for(auto possible_mut : mut_names) + { + if(strcmp(possible_mut.name, mut.c_str()) == 0 && (1 << nextmode) & possible_mut.modes) + { + nextmuts |= possible_mut.val; + found_one = true; + break; + } + } + if(!found_one) + { + conoutft(CON_MESG, "\frmutator \"%s\" not found or incompatible with mode \"%s\"", mut.c_str(), mode_str); + return; + } + } + } + } + + game::nextmode = nextmode; + game::nextmuts = nextmuts; + changemap(map); + } + ICOMMAND(0, start, "sss", (char *map, char *mode, char *muts), startcmd(map, mode, muts)); + void gotoplayer(const char *arg) { if(game::player1.state!=CS_SPECTATOR && game::player1.state!=CS_EDITING) return;