From d5d4ae770852682ee35f90eb9f856aaf73301b50 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 23 Sep 2020 20:33:53 +1000 Subject: [PATCH] Minor cleanup of world.qc, move most intermission and map handling code into intermission.qc --- qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc | 1 + qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc | 1 + .../common/gamemodes/gamemode/race/sv_race.qc | 1 + .../mutators/mutator/sandbox/sv_sandbox.qc | 2 + qcsrc/common/playerstats.qc | 1 + qcsrc/server/_mod.inc | 1 + qcsrc/server/_mod.qh | 1 + qcsrc/server/campaign.qc | 1 + qcsrc/server/client.qc | 1 + qcsrc/server/client.qh | 3 +- qcsrc/server/command/getreplies.qc | 1 + qcsrc/server/command/radarmap.qc | 2 + qcsrc/server/command/sv_cmd.qc | 1 + qcsrc/server/command/sv_cmd.qh | 2 - qcsrc/server/command/vote.qc | 1 + qcsrc/server/hook.qc | 3 +- qcsrc/server/hook.qh | 1 - qcsrc/server/intermission.qc | 479 +++++++++++++++++ qcsrc/server/intermission.qh | 31 ++ qcsrc/server/main.qc | 1 - qcsrc/server/main.qh | 3 + qcsrc/server/mapvoting.qc | 1 + qcsrc/server/miscfunctions.qc | 1 + qcsrc/server/miscfunctions.qh | 1 + qcsrc/server/race.qc | 1 + qcsrc/server/scores.qc | 1 + qcsrc/server/spawnpoints.qc | 1 + qcsrc/server/weapons/weaponstats.qc | 1 + qcsrc/server/world.qc | 480 +----------------- qcsrc/server/world.qh | 16 +- 30 files changed, 544 insertions(+), 497 deletions(-) create mode 100644 qcsrc/server/intermission.qc create mode 100644 qcsrc/server/intermission.qh diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc index 7e4366659..ffde264b9 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc index 1550c8e89..08c4f84be 100644 --- a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/gamemodes/gamemode/race/sv_race.qc b/qcsrc/common/gamemodes/gamemode/race/sv_race.qc index 5c6e52f39..39c72a459 100644 --- a/qcsrc/common/gamemodes/gamemode/race/sv_race.qc +++ b/qcsrc/common/gamemodes/gamemode/race/sv_race.qc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc index 9458189fd..a9c2d3ef0 100644 --- a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc @@ -1,5 +1,7 @@ #include "sv_sandbox.qh" +#include + string autocvar_g_sandbox; int autocvar_g_sandbox_info; bool autocvar_g_sandbox_readonly; diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 3b8c97c4b..0202adb1d 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -8,6 +8,7 @@ #include #include "../server/anticheat.qh" #include + #include #include "../server/scores.qh" #include #include "../server/weapons/accuracy.qh" diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index b4c287a89..cdd7d0d26 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index f9b39314a..7d1728f80 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/campaign.qc b/qcsrc/server/campaign.qc index de46e57d5..3153ff111 100644 --- a/qcsrc/server/campaign.qc +++ b/qcsrc/server/campaign.qc @@ -4,6 +4,7 @@ #include #include "cheats.qh" +#include #include "miscfunctions.qh" #include "world.qh" diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 3afbf4ded..902172cd0 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -16,6 +16,7 @@ #include "damage.qh" #include "handicap.qh" #include "hook.qh" +#include #include "command/common.qh" #include "command/vote.qh" #include "clientkill.qh" diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index a4309822d..d074ebe4b 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -1,6 +1,7 @@ #pragma once #include "utils.qh" +#include #include #include @@ -316,8 +317,6 @@ void ClientData_Touch(entity e); int nJoinAllowed(entity this, entity ignore); -void FixIntermissionClient(entity e); - void checkSpectatorBlock(entity this); void PlayerUseKey(entity this); diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index 326297489..1cf124ad3 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index 6e73777a0..4d3992239 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -6,6 +6,8 @@ #include "../world.qh" +#include + #include #include diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 7de740bdd..281297b04 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -16,6 +16,7 @@ #include "../world.qh" #include "../ipban.qh" #include "../teamplay.qh" +#include #include "../bot/api.qh" diff --git a/qcsrc/server/command/sv_cmd.qh b/qcsrc/server/command/sv_cmd.qh index bf0dafa9c..8beb8fd52 100644 --- a/qcsrc/server/command/sv_cmd.qh +++ b/qcsrc/server/command/sv_cmd.qh @@ -7,8 +7,6 @@ bool shuffleteams_on_reset_map; void shuffleteams(); -string GotoMap(string m); - void race_deleteTime(string map, float pos); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index ee8a8e8db..3d0cb2974 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -12,6 +12,7 @@ #include "common.qh" #include "../damage.qh" +#include #include "../world.qh" #include "../teamplay.qh" #include "../race.qh" diff --git a/qcsrc/server/hook.qc b/qcsrc/server/hook.qc index 6042a48e1..a84a37fc3 100644 --- a/qcsrc/server/hook.qc +++ b/qcsrc/server/hook.qc @@ -414,7 +414,8 @@ void FireGrapplingHook(entity actor, .entity weaponentity) Net_LinkEntity(missile, false, 0, GrapplingHookSend); } -void GrappleHookInit() +// NOTE: using PRECACHE here to make sure it's called after everything else +PRECACHE(GrappleHookInit) { if(g_grappling_hook) { diff --git a/qcsrc/server/hook.qh b/qcsrc/server/hook.qh index 1ed78e274..3be5d6716 100644 --- a/qcsrc/server/hook.qh +++ b/qcsrc/server/hook.qh @@ -18,6 +18,5 @@ const float HOOK_WAITING_FOR_RELEASE = BIT(4); .float hook_state; .int state; -void GrappleHookInit(); vector hook_shotorigin[4]; diff --git a/qcsrc/server/intermission.qc b/qcsrc/server/intermission.qc new file mode 100644 index 000000000..fe2e0dea0 --- /dev/null +++ b/qcsrc/server/intermission.qc @@ -0,0 +1,479 @@ +#include "mapvoting.qh" + +#include +#include +#include +#include +#include +#include +#include +#include + +string GetGametype() +{ + return MapInfo_Type_ToString(MapInfo_LoadedGametype); +} + +string GetMapname() +{ + return mapname; +} + +float Map_Count, Map_Current; +string Map_Current_Name; + +// NOTE: this now expects the map list to be already tokenized and the count in Map_Count +int GetMaplistPosition() +{ + string map = GetMapname(); + int idx = autocvar_g_maplist_index; + + if(idx >= 0) + { + if(idx < Map_Count) + { + if(map == argv(idx)) + { + return idx; + } + } + } + + for(int pos = 0; pos < Map_Count; ++pos) + { + if(map == argv(pos)) + return pos; + } + + // resume normal maplist rotation if current map is not in g_maplist + return idx; +} + +bool MapHasRightSize(string map) +{ + int minplayers = max(0, floor(autocvar_minplayers)); + if (teamplay) + minplayers = max(0, floor(autocvar_minplayers_per_team) * AvailableTeams()); + if (autocvar_g_maplist_check_waypoints + && (currentbots || autocvar_bot_number || player_count < minplayers)) + { + string checkwp_msg = strcat("checkwp ", map); + if(!fexists(strcat("maps/", map, ".waypoints"))) + { + LOG_TRACE(checkwp_msg, ": no waypoints"); + return false; + } + LOG_TRACE(checkwp_msg, ": has waypoints"); + } + + if(autocvar_g_maplist_ignore_sizes) + return true; + + // open map size restriction file + string opensize_msg = strcat("opensize ", map); + float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ); + int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0); + int pcount = ((player_limit > 0) ? min(player_count, player_limit) : player_count); // bind it to the player limit so that forced spectators don't influence the limits + if(!autocvar_g_maplist_sizes_count_bots) + pcount -= currentbots; + if(fh >= 0) + { + opensize_msg = strcat(opensize_msg, ": ok, "); + int mapmin = stoi(fgets(fh)); + int mapmax = stoi(fgets(fh)); + fclose(fh); + if(pcount < mapmin) + { + LOG_TRACE(opensize_msg, "not enough"); + return false; + } + if(mapmax && pcount > mapmax) + { + LOG_TRACE(opensize_msg, "too many"); + return false; + } + LOG_TRACE(opensize_msg, "right size"); + return true; + } + LOG_TRACE(opensize_msg, ": not found"); + return true; +} + +string Map_Filename(int position) +{ + return strcat("maps/", argv(position), ".bsp"); +} + +void Map_MarkAsRecent(string m) +{ + cvar_set("g_maplist_mostrecent", strwords(cons(m, autocvar_g_maplist_mostrecent), max(0, autocvar_g_maplist_mostrecent_count))); +} + +bool Map_IsRecent(string m) +{ + return strhasword(autocvar_g_maplist_mostrecent, m); +} + +bool Map_Check(int position, float pass) +{ + string filename; + string map_next; + map_next = argv(position); + if(pass <= 1) + { + if(Map_IsRecent(map_next)) + return false; + } + filename = Map_Filename(position); + if(MapInfo_CheckMap(map_next)) + { + if(pass == 2) + return true; + if(MapHasRightSize(map_next)) + return true; + return false; + } + else + LOG_DEBUG( "Couldn't select '", filename, "'..." ); + + return false; +} + +void Map_Goto_SetStr(string nextmapname) +{ + if(getmapname_stored != "") + strunzone(getmapname_stored); + if(nextmapname == "") + getmapname_stored = ""; + else + getmapname_stored = strzone(nextmapname); +} + +void Map_Goto_SetFloat(float position) +{ + cvar_set("g_maplist_index", ftos(position)); + Map_Goto_SetStr(argv(position)); +} + +void Map_Goto(float reinit) +{ + MapInfo_LoadMap(getmapname_stored, reinit); +} + +// return codes of map selectors: +// -1 = temporary failure (that is, try some method that is guaranteed to succeed) +// -2 = permanent failure +float MaplistMethod_Iterate() // usual method +{ + float pass, i; + + LOG_TRACE("Trying MaplistMethod_Iterate"); + + for(pass = 1; pass <= 2; ++pass) + { + for(i = 1; i < Map_Count; ++i) + { + float mapindex; + mapindex = (i + Map_Current) % Map_Count; + if(Map_Check(mapindex, pass)) + return mapindex; + } + } + return -1; +} + +float MaplistMethod_Repeat() // fallback method +{ + LOG_TRACE("Trying MaplistMethod_Repeat"); + + if(Map_Check(Map_Current, 2)) + return Map_Current; + return -2; +} + +float MaplistMethod_Random() // random map selection +{ + float i, imax; + + LOG_TRACE("Trying MaplistMethod_Random"); + + imax = 42; + + for(i = 0; i <= imax; ++i) + { + float mapindex; + mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map + if(Map_Check(mapindex, 1)) + return mapindex; + } + return -1; +} + +float MaplistMethod_Shuffle(float exponent) // more clever shuffling +// the exponent sets a bias on the map selection: +// the higher the exponent, the less likely "shortly repeated" same maps are +{ + float i, j, imax, insertpos; + + LOG_TRACE("Trying MaplistMethod_Shuffle"); + + imax = 42; + + for(i = 0; i <= imax; ++i) + { + string newlist; + + // now reinsert this at another position + insertpos = (random() ** (1 / exponent)); // ]0, 1] + insertpos = insertpos * (Map_Count - 1); // ]0, Map_Count - 1] + insertpos = ceil(insertpos) + 1; // {2, 3, 4, ..., Map_Count} + LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos)); + + // insert the current map there + newlist = ""; + for(j = 1; j < insertpos; ++j) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above + newlist = strcat(newlist, " ", argv(j)); + newlist = strcat(newlist, " ", argv(0)); // now insert the just selected map + for(j = insertpos; j < Map_Count; ++j) // i == Map_Count: no loop, has just been inserted as last + newlist = strcat(newlist, " ", argv(j)); + newlist = substring(newlist, 1, strlen(newlist) - 1); + cvar_set("g_maplist", newlist); + Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); + + // NOTE: the selected map has just been inserted at (insertpos-1)th position + Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working + if(Map_Check(Map_Current, 1)) + return Map_Current; + } + return -1; +} + +void Maplist_Init() +{ + float i = Map_Count = 0; + if(autocvar_g_maplist != "") + { + Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); + for (i = 0; i < Map_Count; ++i) + { + if (Map_Check(i, 2)) + break; + } + } + + if (i == Map_Count) + { + bprint( "Maplist contains no usable maps! Resetting it to default map list.\n" ); + cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST)); + if(autocvar_g_maplist_shuffle) + ShuffleMaplist(); + if(!server_is_dedicated) + localcmd("\nmenu_cmd sync\n"); + Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); + } + if(Map_Count == 0) + error("empty maplist, cannot select a new map"); + Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1); + + strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP + // this may or may not be correct, but who cares, in the worst case a map + // isn't chosen in the first pass that should have been +} + +string GetNextMap() +{ + Maplist_Init(); + float nextMap = -1; + + if(nextMap == -1) + if(autocvar_g_maplist_shuffle > 0) + nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1); + + if(nextMap == -1) + if(autocvar_g_maplist_selectrandom) + nextMap = MaplistMethod_Random(); + + if(nextMap == -1) + nextMap = MaplistMethod_Iterate(); + + if(nextMap == -1) + nextMap = MaplistMethod_Repeat(); + + if(nextMap >= 0) + { + Map_Goto_SetFloat(nextMap); + return getmapname_stored; + } + + return ""; +} + +float DoNextMapOverride(float reinit) +{ + if(autocvar_g_campaign) + { + CampaignPostIntermission(); + alreadychangedlevel = true; + return true; + } + if(autocvar_quit_when_empty) + { + if(player_count <= currentbots) + { + localcmd("quit\n"); + alreadychangedlevel = true; + return true; + } + } + if(autocvar_quit_and_redirect != "") + { + redirection_target = strzone(autocvar_quit_and_redirect); + alreadychangedlevel = true; + return true; + } + if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level + { + localcmd("restart\n"); + alreadychangedlevel = true; + return true; + } + if(autocvar_nextmap != "") + { + string m; + m = GameTypeVote_MapInfo_FixName(autocvar_nextmap); + cvar_set("nextmap",m); + + if(!m || gametypevote) + return false; + if(autocvar_sv_vote_gametype) + { + Map_Goto_SetStr(m); + return false; + } + + if(MapInfo_CheckMap(m)) + { + Map_Goto_SetStr(m); + Map_Goto(reinit); + alreadychangedlevel = true; + return true; + } + } + if(!reinit && autocvar_lastlevel) + { + cvar_settemp_restore(); + localcmd("set lastlevel 0\ntogglemenu 1\n"); + alreadychangedlevel = true; + return true; + } + return false; +} + +void GotoNextMap(float reinit) +{ + //string nextmap; + //float n, nummaps; + //string s; + if (alreadychangedlevel) + return; + alreadychangedlevel = true; + + string nextMap = GetNextMap(); + if(nextMap == "") + error("Everything is broken - cannot find a next map. Please report this to the developers."); + Map_Goto(reinit); +} + +void ShuffleMaplist() +{ + cvar_set("g_maplist", shufflewords(autocvar_g_maplist)); +} + +string GotoMap(string m) +{ + m = GameTypeVote_MapInfo_FixName(m); + if (!m) + return "The map you suggested is not available on this server."; + if (!autocvar_sv_vote_gametype) + if(!MapInfo_CheckMap(m)) + return "The map you suggested does not support the current game mode."; + cvar_set("nextmap", m); + cvar_set("timelimit", "-1"); + if(mapvote_initialized || alreadychangedlevel) + { + if(DoNextMapOverride(0)) + return "Map switch initiated."; + else + return "Hm... no. For some reason I like THIS map more."; + } + else + return "Map switch will happen after scoreboard."; +} + + +/* +============ +IntermissionThink + +When the player presses attack or jump, change to the next level +============ +*/ +.float autoscreenshot; +void IntermissionThink(entity this) +{ + FixIntermissionClient(this); + + float server_screenshot = (autocvar_sv_autoscreenshot && CS(this).cvar_cl_autoscreenshot); + float client_screenshot = (CS(this).cvar_cl_autoscreenshot == 2); + + if( (server_screenshot || client_screenshot) + && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) ) + { + this.autoscreenshot = -1; + if(IS_REAL_CLIENT(this)) { stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); } + return; + } + + if (time < intermission_exittime) + return; + + if(!mapvote_initialized) + if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this))) + return; + + MapVote_Start(); +} + +void FixIntermissionClient(entity e) +{ + if(!e.autoscreenshot) // initial call + { + e.autoscreenshot = time + 0.8; // used for autoscreenshot + SetResourceExplicit(e, RES_HEALTH, -2342); + // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not) + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(e.(weaponentity)) + { + e.(weaponentity).effects = EF_NODRAW; + if (e.(weaponentity).weaponchild) + e.(weaponentity).weaponchild.effects = EF_NODRAW; + } + } + if(IS_REAL_CLIENT(e)) + { + stuffcmd(e, "\nscr_printspeed 1000000\n"); + RandomSelection_Init(); + FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, { + RandomSelection_AddString(it, 1, 1); + }); + if (RandomSelection_chosen_string != "") + { + stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string)); + } + msg_entity = e; + WriteByte(MSG_ONE, SVC_INTERMISSION); + } + } +} diff --git a/qcsrc/server/intermission.qh b/qcsrc/server/intermission.qh new file mode 100644 index 000000000..03458eb79 --- /dev/null +++ b/qcsrc/server/intermission.qh @@ -0,0 +1,31 @@ +#pragma once + +float intermission_running; +float intermission_exittime; +float alreadychangedlevel; + +string GetGametype(); + +string GetMapname(); + +void IntermissionThink(entity this); + +void FixIntermissionClient(entity e); + +void GotoNextMap(float reinit); + +bool Map_IsRecent(string m); + +string GetNextMap(); + +void ShuffleMaplist(); + +void Map_Goto_SetStr(string nextmapname); + +void Map_Goto(float reinit); + +void Map_MarkAsRecent(string m); + +float DoNextMapOverride(float reinit); + +string GotoMap(string m); diff --git a/qcsrc/server/main.qc b/qcsrc/server/main.qc index 3db2a0f8e..0983d4621 100644 --- a/qcsrc/server/main.qc +++ b/qcsrc/server/main.qc @@ -209,7 +209,6 @@ void make_safe_for_remove(entity e) } } -.float remove_except_protected_forbidden; void remove_except_protected(entity e) { if(e.remove_except_protected_forbidden) diff --git a/qcsrc/server/main.qh b/qcsrc/server/main.qh index 515ec62e6..a8c86fe2f 100644 --- a/qcsrc/server/main.qh +++ b/qcsrc/server/main.qh @@ -3,6 +3,9 @@ /** print(), but only print if the server is not local */ void dedicated_print(string input); +.float remove_except_protected_forbidden; +void remove_except_protected(entity e); + void remove_safely(entity e); void remove_unsafely(entity e); diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc index ac64f630f..bdd563b81 100644 --- a/qcsrc/server/mapvoting.qc +++ b/qcsrc/server/mapvoting.qc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "world.qh" #include "command/cmd.qh" diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index df830d13e..b748c17a0 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -8,6 +8,7 @@ #include "world.qh" #include #include "ipban.qh" +#include #include #include #include diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index 82618160a..0f37cfac7 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index c2de3c9cc..5014e02ae 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index e4e1d236a..7bd776989 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -3,6 +3,7 @@ #include "command/common.qh" #include #include "client.qh" +#include #include #include #include diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index b629ef168..532d63118 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -16,6 +16,7 @@ #include "../common/util.qh" #include "../lib/warpzone/common.qh" #include "../lib/warpzone/util_server.qh" +#include #include #include diff --git a/qcsrc/server/weapons/weaponstats.qc b/qcsrc/server/weapons/weaponstats.qc index 552ab6731..02ba234ea 100644 --- a/qcsrc/server/weapons/weaponstats.qc +++ b/qcsrc/server/weapons/weaponstats.qc @@ -1,5 +1,6 @@ #include "weaponstats.qh" +#include #include #include #include diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 89c478ab7..329aab087 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -14,6 +14,8 @@ #include #include #include "ipban.qh" +#include +#include #include "mapvoting.qh" #include #include "race.qh" @@ -96,7 +98,6 @@ void PingPLReport_Spawn() } const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1; -string redirection_target; float world_initialized; void SetDefaultAlpha() @@ -627,7 +628,7 @@ STATIC_INIT_EARLY(maxclients) } } -void default_delayedinit(entity this) +void GameplayMode_DelayedInit(entity this) { if(!scores_initialized) ScoreRules_generic(); @@ -677,11 +678,10 @@ void InitGameplayMode() cache_mutatormsg = strzone(""); cache_lastmutatormsg = strzone(""); - InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK); + InitializeEntity(NULL, GameplayMode_DelayedInit, INITPRIO_GAMETYPE_FALLBACK); } -void Map_MarkAsRecent(string m); -float world_already_spawned; +bool world_already_spawned; spawnfunc(worldspawn) { server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated"))); @@ -814,7 +814,6 @@ spawnfunc(worldspawn) static_init_late(); static_init_precache(); readlevelcvars(); - GrappleHookInit(); GameRules_limit_fallbacks(); @@ -1020,415 +1019,6 @@ spawnfunc(light) delete(this); } -string GetGametype() -{ - return MapInfo_Type_ToString(MapInfo_LoadedGametype); -} - -string GetMapname() -{ - return mapname; -} - -float Map_Count, Map_Current; -string Map_Current_Name; - -// NOTE: this now expects the map list to be already tokenized and the count in Map_Count -int GetMaplistPosition() -{ - string map = GetMapname(); - int idx = autocvar_g_maplist_index; - - if(idx >= 0) - { - if(idx < Map_Count) - { - if(map == argv(idx)) - { - return idx; - } - } - } - - for(int pos = 0; pos < Map_Count; ++pos) - { - if(map == argv(pos)) - return pos; - } - - // resume normal maplist rotation if current map is not in g_maplist - return idx; -} - -bool MapHasRightSize(string map) -{ - int minplayers = max(0, floor(autocvar_minplayers)); - if (teamplay) - minplayers = max(0, floor(autocvar_minplayers_per_team) * AvailableTeams()); - if (autocvar_g_maplist_check_waypoints - && (currentbots || autocvar_bot_number || player_count < minplayers)) - { - string checkwp_msg = strcat("checkwp ", map); - if(!fexists(strcat("maps/", map, ".waypoints"))) - { - LOG_TRACE(checkwp_msg, ": no waypoints"); - return false; - } - LOG_TRACE(checkwp_msg, ": has waypoints"); - } - - if(autocvar_g_maplist_ignore_sizes) - return true; - - // open map size restriction file - string opensize_msg = strcat("opensize ", map); - float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ); - int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0); - int pcount = ((player_limit > 0) ? min(player_count, player_limit) : player_count); // bind it to the player limit so that forced spectators don't influence the limits - if(!autocvar_g_maplist_sizes_count_bots) - pcount -= currentbots; - if(fh >= 0) - { - opensize_msg = strcat(opensize_msg, ": ok, "); - int mapmin = stoi(fgets(fh)); - int mapmax = stoi(fgets(fh)); - fclose(fh); - if(pcount < mapmin) - { - LOG_TRACE(opensize_msg, "not enough"); - return false; - } - if(mapmax && pcount > mapmax) - { - LOG_TRACE(opensize_msg, "too many"); - return false; - } - LOG_TRACE(opensize_msg, "right size"); - return true; - } - LOG_TRACE(opensize_msg, ": not found"); - return true; -} - -string Map_Filename(float position) -{ - return strcat("maps/", argv(position), ".bsp"); -} - -void Map_MarkAsRecent(string m) -{ - cvar_set("g_maplist_mostrecent", strwords(cons(m, autocvar_g_maplist_mostrecent), max(0, autocvar_g_maplist_mostrecent_count))); -} - -float Map_IsRecent(string m) -{ - return strhasword(autocvar_g_maplist_mostrecent, m); -} - -float Map_Check(float position, float pass) -{ - string filename; - string map_next; - map_next = argv(position); - if(pass <= 1) - { - if(Map_IsRecent(map_next)) - return 0; - } - filename = Map_Filename(position); - if(MapInfo_CheckMap(map_next)) - { - if(pass == 2) - return 1; - if(MapHasRightSize(map_next)) - return 1; - return 0; - } - else - LOG_DEBUG( "Couldn't select '", filename, "'..." ); - - return 0; -} - -void Map_Goto_SetStr(string nextmapname) -{ - if(getmapname_stored != "") - strunzone(getmapname_stored); - if(nextmapname == "") - getmapname_stored = ""; - else - getmapname_stored = strzone(nextmapname); -} - -void Map_Goto_SetFloat(float position) -{ - cvar_set("g_maplist_index", ftos(position)); - Map_Goto_SetStr(argv(position)); -} - -void Map_Goto(float reinit) -{ - MapInfo_LoadMap(getmapname_stored, reinit); -} - -// return codes of map selectors: -// -1 = temporary failure (that is, try some method that is guaranteed to succeed) -// -2 = permanent failure -float MaplistMethod_Iterate() // usual method -{ - float pass, i; - - LOG_TRACE("Trying MaplistMethod_Iterate"); - - for(pass = 1; pass <= 2; ++pass) - { - for(i = 1; i < Map_Count; ++i) - { - float mapindex; - mapindex = (i + Map_Current) % Map_Count; - if(Map_Check(mapindex, pass)) - return mapindex; - } - } - return -1; -} - -float MaplistMethod_Repeat() // fallback method -{ - LOG_TRACE("Trying MaplistMethod_Repeat"); - - if(Map_Check(Map_Current, 2)) - return Map_Current; - return -2; -} - -float MaplistMethod_Random() // random map selection -{ - float i, imax; - - LOG_TRACE("Trying MaplistMethod_Random"); - - imax = 42; - - for(i = 0; i <= imax; ++i) - { - float mapindex; - mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map - if(Map_Check(mapindex, 1)) - return mapindex; - } - return -1; -} - -float MaplistMethod_Shuffle(float exponent) // more clever shuffling -// the exponent sets a bias on the map selection: -// the higher the exponent, the less likely "shortly repeated" same maps are -{ - float i, j, imax, insertpos; - - LOG_TRACE("Trying MaplistMethod_Shuffle"); - - imax = 42; - - for(i = 0; i <= imax; ++i) - { - string newlist; - - // now reinsert this at another position - insertpos = (random() ** (1 / exponent)); // ]0, 1] - insertpos = insertpos * (Map_Count - 1); // ]0, Map_Count - 1] - insertpos = ceil(insertpos) + 1; // {2, 3, 4, ..., Map_Count} - LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos)); - - // insert the current map there - newlist = ""; - for(j = 1; j < insertpos; ++j) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above - newlist = strcat(newlist, " ", argv(j)); - newlist = strcat(newlist, " ", argv(0)); // now insert the just selected map - for(j = insertpos; j < Map_Count; ++j) // i == Map_Count: no loop, has just been inserted as last - newlist = strcat(newlist, " ", argv(j)); - newlist = substring(newlist, 1, strlen(newlist) - 1); - cvar_set("g_maplist", newlist); - Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); - - // NOTE: the selected map has just been inserted at (insertpos-1)th position - Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working - if(Map_Check(Map_Current, 1)) - return Map_Current; - } - return -1; -} - -void Maplist_Init() -{ - float i = Map_Count = 0; - if(autocvar_g_maplist != "") - { - Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); - for (i = 0; i < Map_Count; ++i) - { - if (Map_Check(i, 2)) - break; - } - } - - if (i == Map_Count) - { - bprint( "Maplist contains no usable maps! Resetting it to default map list.\n" ); - cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST)); - if(autocvar_g_maplist_shuffle) - ShuffleMaplist(); - if(!server_is_dedicated) - localcmd("\nmenu_cmd sync\n"); - Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); - } - if(Map_Count == 0) - error("empty maplist, cannot select a new map"); - Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1); - - strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP - // this may or may not be correct, but who cares, in the worst case a map - // isn't chosen in the first pass that should have been -} - -string GetNextMap() -{ - Maplist_Init(); - float nextMap = -1; - - if(nextMap == -1) - if(autocvar_g_maplist_shuffle > 0) - nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1); - - if(nextMap == -1) - if(autocvar_g_maplist_selectrandom) - nextMap = MaplistMethod_Random(); - - if(nextMap == -1) - nextMap = MaplistMethod_Iterate(); - - if(nextMap == -1) - nextMap = MaplistMethod_Repeat(); - - if(nextMap >= 0) - { - Map_Goto_SetFloat(nextMap); - return getmapname_stored; - } - - return ""; -} - -float DoNextMapOverride(float reinit) -{ - if(autocvar_g_campaign) - { - CampaignPostIntermission(); - alreadychangedlevel = true; - return true; - } - if(autocvar_quit_when_empty) - { - if(player_count <= currentbots) - { - localcmd("quit\n"); - alreadychangedlevel = true; - return true; - } - } - if(autocvar_quit_and_redirect != "") - { - redirection_target = strzone(autocvar_quit_and_redirect); - alreadychangedlevel = true; - return true; - } - if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level - { - localcmd("restart\n"); - alreadychangedlevel = true; - return true; - } - if(autocvar_nextmap != "") - { - string m; - m = GameTypeVote_MapInfo_FixName(autocvar_nextmap); - cvar_set("nextmap",m); - - if(!m || gametypevote) - return false; - if(autocvar_sv_vote_gametype) - { - Map_Goto_SetStr(m); - return false; - } - - if(MapInfo_CheckMap(m)) - { - Map_Goto_SetStr(m); - Map_Goto(reinit); - alreadychangedlevel = true; - return true; - } - } - if(!reinit && autocvar_lastlevel) - { - cvar_settemp_restore(); - localcmd("set lastlevel 0\ntogglemenu 1\n"); - alreadychangedlevel = true; - return true; - } - return false; -} - -void GotoNextMap(float reinit) -{ - //string nextmap; - //float n, nummaps; - //string s; - if (alreadychangedlevel) - return; - alreadychangedlevel = true; - - string nextMap = GetNextMap(); - if(nextMap == "") - error("Everything is broken - cannot find a next map. Please report this to the developers."); - Map_Goto(reinit); -} - - -/* -============ -IntermissionThink - -When the player presses attack or jump, change to the next level -============ -*/ -.float autoscreenshot; -void IntermissionThink(entity this) -{ - FixIntermissionClient(this); - - float server_screenshot = (autocvar_sv_autoscreenshot && CS(this).cvar_cl_autoscreenshot); - float client_screenshot = (CS(this).cvar_cl_autoscreenshot == 2); - - if( (server_screenshot || client_screenshot) - && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) ) - { - this.autoscreenshot = -1; - if(IS_REAL_CLIENT(this)) { stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); } - return; - } - - if (time < intermission_exittime) - return; - - if(!mapvote_initialized) - if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this))) - return; - - MapVote_Start(); -} - /* =============================================================================== @@ -1539,40 +1129,6 @@ void DumpStats(float final) } } -void FixIntermissionClient(entity e) -{ - if(!e.autoscreenshot) // initial call - { - e.autoscreenshot = time + 0.8; // used for autoscreenshot - SetResourceExplicit(e, RES_HEALTH, -2342); - // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not) - for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(e.(weaponentity)) - { - e.(weaponentity).effects = EF_NODRAW; - if (e.(weaponentity).weaponchild) - e.(weaponentity).weaponchild.effects = EF_NODRAW; - } - } - if(IS_REAL_CLIENT(e)) - { - stuffcmd(e, "\nscr_printspeed 1000000\n"); - RandomSelection_Init(); - FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, { - RandomSelection_AddString(it, 1, 1); - }); - if (RandomSelection_chosen_string != "") - { - stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string)); - } - msg_entity = e; - WriteByte(MSG_ONE, SVC_INTERMISSION); - } - } -} - /* go to the next level for deathmatch only called if a time or frag limit has expired @@ -1713,11 +1269,6 @@ void ClearWinners() FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; }); } -void ShuffleMaplist() -{ - cvar_set("g_maplist", shufflewords(autocvar_g_maplist)); -} - int fragsleft_last; float WinningCondition_Scores(float limit, float leadlimit) { @@ -2017,27 +1568,6 @@ void CheckRules_World() } } -string GotoMap(string m) -{ - m = GameTypeVote_MapInfo_FixName(m); - if (!m) - return "The map you suggested is not available on this server."; - if (!autocvar_sv_vote_gametype) - if(!MapInfo_CheckMap(m)) - return "The map you suggested does not support the current game mode."; - cvar_set("nextmap", m); - cvar_set("timelimit", "-1"); - if(mapvote_initialized || alreadychangedlevel) - { - if(DoNextMapOverride(0)) - return "Map switch initiated."; - else - return "Hm... no. For some reason I like THIS map more."; - } - else - return "Map switch will happen after scoreboard."; -} - bool autocvar_sv_gameplayfix_multiplethinksperframe = true; void RunThink(entity this) { diff --git a/qcsrc/server/world.qh b/qcsrc/server/world.qh index f49346a1c..3d8ca9973 100644 --- a/qcsrc/server/world.qh +++ b/qcsrc/server/world.qh @@ -22,9 +22,7 @@ string matchid; .string fog; -float intermission_running; -float intermission_exittime; -float alreadychangedlevel; +string redirection_target; string cache_mutatormsg; string cache_lastmutatormsg; @@ -44,23 +42,11 @@ const int WINNING_STARTSUDDENDEATHOVERTIME = 3; // no winner, enter suddendeath float WinningCondition_Scores(float limit, float leadlimit); void SetWinners(.float field, float value); -void IntermissionThink(entity this); -void GotoNextMap(float reinit); void ReadyRestart(); -string GetGametype(); - void DumpStats(float final); -float Map_IsRecent(string m); -string GetNextMap(); -void ShuffleMaplist(); -void Map_Goto_SetStr(string nextmapname); -void Map_Goto(float reinit); -void Map_MarkAsRecent(string m); -float DoNextMapOverride(float reinit); void CheckRules_World(); float RedirectionThink(); -string GetMapname(); IntrusiveList g_moveables; STATIC_INIT(g_moveables) { g_moveables = IL_NEW(); } -- 2.39.2