Skip to content

Commit

Permalink
Add in coaches_per_team config (default 2).
Browse files Browse the repository at this point in the history
Update docs.

Change g_TeamCoaches to ArrayList like player auths.

Update teamjoin logic accordingly based on max coaches per team.
  • Loading branch information
PhlexPlexico committed Jun 27, 2022
1 parent a32b867 commit a0ad3f8
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 35 deletions.
1 change: 1 addition & 0 deletions configs/get5/example_match.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
}

"players_per_team" "5"
"coaches_per_team" "2"
"min_players_to_ready" "1" // Minimum # of players a team must have to ready
"min_spectators_to_ready" "0" // How many spectators must be ready to begin.

Expand Down
1 change: 1 addition & 0 deletions configs/get5/example_match.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"matchid": "example_match",
"num_maps": 3,
"players_per_team": 1,
"coaches_per_team": 2,
"min_players_to_ready": 1,
"min_spectators_to_ready": 0,
"skip_veto": false,
Expand Down
1 change: 1 addition & 0 deletions configs/get5/scrim_template.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"scrim" "1"
"side_type" "never_knife"
"players_per_team" "5"
"coaches_per_team" "2"
"num_maps" "1"
"skip_veto" "1"

Expand Down
2 changes: 2 additions & 0 deletions documentation/docs/match_schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ There are quite a few values that are also optional within the team schema, but
- `players`: A list of Steam ID's for users on the team (not used if `get5_check_auths` is set to `0`). You can also force player names in here; in JSON you may use either an array of steamids or a dictionary of Steam IDs to names. Both ways are shown in the above example.
- `series_score`: The current score in the series, this can be used to give a team a map advantage or used as a manual backup method, defaults to `0`.
- `matchtext`: Wraps `mp_teammatchstat_1`, you probably don't want to set this, in BoX series `mp_teamscore` cvars are automatically set and take the place of the `mp_teammatchstat` cvars.
- `coaches`: Identical to the `players` tag, it's an optional list of Steam ID's for users who wish to coach a team. You may also force player names here. This field is optional.

## Optional Values
- `matchid`: A string matchid used to identify the match.
Expand All @@ -20,6 +21,7 @@ There are quite a few values that are also optional within the team schema, but
- `veto_first`: Either "team1", or "team2". If not set, or set to any other value, "team1" will veto first.
- `side_type`: Either "standard", "never_knife", or "always_knife"; "standard" means the team that doesn't pick a map gets the side choice, "never_knife" means "team1" is always on CT first, and "always_knife" means there is always a knife round.
- `players_per_team`: Maximum players per team (doesn't include a coach spot, default: 5).
- `coaches_per_team`: Maximum coaches per team (default: 2).
- `min_players_to_ready`: Minimum players a team needs to be able to ready up (default: 1).
- `favored_percentage_team1`: Wrapper for the servers `mp_teamprediction_pct`.
- `favored_percentage_text` Wrapper for the servers `mp_teamprediction_txt`.
Expand Down
5 changes: 4 additions & 1 deletion scripting/get5.sp
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,19 @@ bool g_BO2Match = false;
char g_MatchID[MATCH_ID_LENGTH];
ArrayList g_MapPoolList = null;
ArrayList g_TeamAuths[MATCHTEAM_COUNT];
ArrayList g_TeamCoaches[MATCHTEAM_COUNT];
StringMap g_PlayerNames;
char g_TeamNames[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_TeamTags[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_FormattedTeamNames[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_TeamFlags[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_TeamLogos[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_TeamMatchTexts[MATCHTEAM_COUNT][MAX_CVAR_LENGTH];
char g_TeamCoaches[MATCHTEAM_COUNT][64];
char g_MatchTitle[MAX_CVAR_LENGTH];
int g_FavoredTeamPercentage = 0;
char g_FavoredTeamText[MAX_CVAR_LENGTH];
int g_PlayersPerTeam = 5;
int g_CoachesPerTeam = 2;
int g_MinPlayersToReady = 1;
int g_MinSpectatorsToReady = 0;
bool g_SkipVeto = false;
Expand Down Expand Up @@ -486,6 +487,8 @@ public void OnPluginStart() {

for (int i = 0; i < sizeof(g_TeamAuths); i++) {
g_TeamAuths[i] = new ArrayList(AUTH_LENGTH);
// Same length.
g_TeamCoaches[i] = new ArrayList(AUTH_LENGTH);
}
g_PlayerNames = new StringMap();

Expand Down
3 changes: 2 additions & 1 deletion scripting/get5/debug.sp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ static void AddGlobalStateInfo(File f) {

f.WriteLine("g_MatchTitle = %s", g_MatchTitle);
f.WriteLine("g_PlayersPerTeam = %d", g_PlayersPerTeam);
f.WriteLine("g_CoachesPerTeam = %d", g_CoachesPerTeam);
f.WriteLine("g_MinPlayersToReady = %d", g_MinPlayersToReady);
f.WriteLine("g_MinSpectatorsToReady = %d", g_MinSpectatorsToReady);
f.WriteLine("g_SkipVeto = %d", g_SkipVeto);
Expand Down Expand Up @@ -137,7 +138,7 @@ static void AddGlobalStateInfo(File f) {
f.WriteLine("g_TeamPausesUsed = %d", g_TeamPausesUsed[team]);
f.WriteLine("g_TeamTechPausesUsed = %d", g_TeamTechPausesUsed[team]);
f.WriteLine("g_TeamGivenTechPauseCommand = %d", g_TeamGivenTechPauseCommand[team]);
f.WriteLine("g_TeamCoaches = %s", g_TeamCoaches[team]);
WriteArrayList(f, "g_TeamCoaches", g_TeamCoaches[team]);
}
}

Expand Down
53 changes: 28 additions & 25 deletions scripting/get5/matchconfig.sp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define CONFIG_MATCHID_DEFAULT "matchid"
#define CONFIG_MATCHTITLE_DEFAULT "Map {MAPNUMBER} of {MAXMAPS}"
#define CONFIG_PLAYERSPERTEAM_DEFAULT 5
#define CONFIG_COACHESPERTEAM_DEFAULT 2
#define CONFIG_MINPLAYERSTOREADY_DEFAULT 0
#define CONFIG_MINSPECTATORSTOREADY_DEFAULT 0
#define CONFIG_SPECTATORSNAME_DEFAULT "casters"
Expand All @@ -27,7 +28,7 @@ stock bool LoadMatchConfig(const char[] config, bool restoreBackup = false) {
g_TeamTechPausesUsed[team] = 0;
g_TechPausedTimeOverride[team] = 0;
g_TeamGivenTechPauseCommand[team] = false;
g_TeamCoaches[team] = "";
ClearArray(GetTeamCoaches(team));
ClearArray(GetTeamAuths(team));
}

Expand Down Expand Up @@ -270,6 +271,7 @@ public void WriteMatchToKv(KeyValues kv) {
kv.SetNum("bo2_series", g_BO2Match);
kv.SetNum("skip_veto", g_SkipVeto);
kv.SetNum("players_per_team", g_PlayersPerTeam);
kv.SetNum("coaches_per_team", g_CoachesPerTeam);
kv.SetNum("min_players_to_ready", g_MinPlayersToReady);
kv.SetNum("min_spectators_to_ready", g_MinSpectatorsToReady);
kv.SetString("match_title", g_MatchTitle);
Expand Down Expand Up @@ -331,10 +333,13 @@ static void AddTeamBackupData(KeyValues kv, MatchTeam team) {
kv.SetString("flag", g_TeamFlags[team]);
kv.SetString("logo", g_TeamLogos[team]);
kv.SetString("matchtext", g_TeamMatchTexts[team]);
if (!StrEqual(g_TeamCoaches[team], "")) {
kv.JumpToKey("coach", true);
kv.SetString(g_TeamCoaches[team], KEYVALUE_STRING_PLACEHOLDER);
kv.GoBack();
kv.JumpToKey("coaches", true);
for (int i = 0; i < GetTeamCoaches(team).Length; i++) {
GetTeamCoaches(team).GetString(i, auth, sizeof(auth));
if (!g_PlayerNames.GetString(auth, name, sizeof(name))) {
strcopy(name, sizeof(name), KEYVALUE_STRING_PLACEHOLDER);
}
kv.SetString(auth, KEYVALUE_STRING_PLACEHOLDER);
}
}
}
Expand All @@ -344,6 +349,7 @@ static bool LoadMatchFromKv(KeyValues kv) {
g_InScrimMode = kv.GetNum("scrim") != 0;
kv.GetString("match_title", g_MatchTitle, sizeof(g_MatchTitle), CONFIG_MATCHTITLE_DEFAULT);
g_PlayersPerTeam = kv.GetNum("players_per_team", CONFIG_PLAYERSPERTEAM_DEFAULT);
g_CoachesPerTeam = kv.GetNum("coaches_per_team", CONFIG_COACHESPERTEAM_DEFAULT);
g_MinPlayersToReady = kv.GetNum("min_players_to_ready", CONFIG_MINPLAYERSTOREADY_DEFAULT);
g_MinSpectatorsToReady =
kv.GetNum("min_spectators_to_ready", CONFIG_MINSPECTATORSTOREADY_DEFAULT);
Expand Down Expand Up @@ -455,6 +461,8 @@ static bool LoadMatchFromJson(JSON_Object json) {

g_PlayersPerTeam =
json_object_get_int_safe(json, "players_per_team", CONFIG_PLAYERSPERTEAM_DEFAULT);
g_CoachesPerTeam =
json_object_get_int_safe(json, "coaches_per_team", CONFIG_COACHESPERTEAM_DEFAULT);
g_MinPlayersToReady =
json_object_get_int_safe(json, "min_players_to_ready", CONFIG_MINPLAYERSTOREADY_DEFAULT);
g_MinSpectatorsToReady = json_object_get_int_safe(json, "min_spectators_to_ready",
Expand Down Expand Up @@ -575,16 +583,16 @@ static void LoadTeamDataJson(JSON_Object json, MatchTeam matchTeam) {
if (StrEqual(fromfile, "")) {
// TODO: this needs to support both an array and a dictionary
// For now, it only supports an array
char tmpCoach[AUTH_LENGTH];
JSON_Object coaches = json.GetObject("coaches");
AddJsonAuthsToList(json, "players", GetTeamAuths(matchTeam), AUTH_LENGTH);
if (coaches != null) {
AddJsonAuthsToList(json, "coaches", GetTeamCoaches(matchTeam), AUTH_LENGTH);
}
json_object_get_string_safe(json, "name", g_TeamNames[matchTeam], MAX_CVAR_LENGTH);
json_object_get_string_safe(json, "tag", g_TeamTags[matchTeam], MAX_CVAR_LENGTH);
json_object_get_string_safe(json, "flag", g_TeamFlags[matchTeam], MAX_CVAR_LENGTH);
json_object_get_string_safe(json, "logo", g_TeamLogos[matchTeam], MAX_CVAR_LENGTH);
json_object_get_string_safe(json, "matchtext", g_TeamMatchTexts[matchTeam], MAX_CVAR_LENGTH);
// Since we can have any steam auth come in, we need to convert this as well.
json_object_get_string_safe(json, "coach", tmpCoach, AUTH_LENGTH);
ConvertAuthToSteam64(tmpCoach, g_TeamCoaches[matchTeam]);
} else {
JSON_Object fromfileJson = json_load_file(fromfile);
if (fromfileJson == null) {
Expand All @@ -607,21 +615,16 @@ static void LoadTeamData(KeyValues kv, MatchTeam matchTeam) {
kv.GetString("fromfile", fromfile, sizeof(fromfile));

if (StrEqual(fromfile, "")) {
char tmpCoach[AUTH_LENGTH];
AddSubsectionAuthsToList(kv, "players", GetTeamAuths(matchTeam), AUTH_LENGTH);
if (kv.JumpToKey("coaches")) {
AddSubsectionAuthsToList(kv, "coaches", GetTeamCoaches(matchTeam), AUTH_LENGTH);
kv.GoBack();
}
kv.GetString("name", g_TeamNames[matchTeam], MAX_CVAR_LENGTH, "");
kv.GetString("tag", g_TeamTags[matchTeam], MAX_CVAR_LENGTH, "");
kv.GetString("flag", g_TeamFlags[matchTeam], MAX_CVAR_LENGTH, "");
kv.GetString("logo", g_TeamLogos[matchTeam], MAX_CVAR_LENGTH, "");
kv.GetString("matchtext", g_TeamMatchTexts[matchTeam], MAX_CVAR_LENGTH, "");
if (kv.JumpToKey("coach")) {
if(kv.GotoFirstSubKey(false)) {
kv.GetSectionName(tmpCoach, AUTH_LENGTH);
ConvertAuthToSteam64(tmpCoach, g_TeamCoaches[matchTeam]);
kv.GoBack();
}
kv.GoBack();
}
} else {
KeyValues fromfilekv = new KeyValues("team");
if (fromfilekv.ImportFromFile(fromfile)) {
Expand Down Expand Up @@ -858,19 +861,19 @@ public Action Command_AddCoach(int client, int args) {
return Plugin_Handled;
}

if (!StrEqual(g_TeamCoaches[team], "")) {
ReplyToCommand(client, "There is already a coach on that team.");
if (GetTeamCoaches(team).Length == g_CoachesPerTeam) {
ReplyToCommand(client, "Coach Spots are full for team %s", teamString);
return Plugin_Handled;
}

ConvertAuthToSteam64(auth, steam64);
if(!ConvertAuthToSteam64(auth, steam64)) {
return Plugin_Handled;
}

if (AddPlayerToTeam(auth, team, name)) {
strcopy(g_TeamCoaches[team], AUTH_LENGTH, steam64);
if (AddCoachToTeam(auth, team, name)) {
ReplyToCommand(client, "Successfully added player %s to coach team %s", auth, teamString);
} else {
ReplyToCommand(client, "Player %s is already on a match team, setting them as coach.", auth);
strcopy(g_TeamCoaches[team], AUTH_LENGTH, steam64);
ReplyToCommand(client, "Player %s is already in a coaching position on a team.", auth);
}
} else {
ReplyToCommand(client, "Usage: get5_addcoach <auth> <team1|team2> [name]");
Expand Down
63 changes: 55 additions & 8 deletions scripting/get5/teamlogic.sp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void CheckClientTeam(int client) {
} else if (GetAuth(client, auth, sizeof(auth))) {
char steam64[AUTH_LENGTH];
ConvertAuthToSteam64(auth, steam64);
if (StrEqual(g_TeamCoaches[csTeam], steam64)) {
if (IsAuthOnTeamCoach(steam64, correctTeam)) {
UpdateCoachTarget(client, csTeam);
}
}
Expand Down Expand Up @@ -85,8 +85,8 @@ public Action Command_JoinTeam(int client, const char[] command, int argc) {
if (!g_CoachingEnabledCvar.BoolValue) {
KickClient(client, "%t", "TeamIsFullInfoMessage");
} else {
// Only attempt swapping if the coach slot is empty.
if (StrEqual(g_TeamCoaches[correctTeam], "")) {
// Only attempt to move to coach if we are not full on coaches already.
if (GetTeamCoaches(correctTeam).Length < g_CoachesPerTeam) {
LogDebug("Forcing player %N to coach", client);
MoveClientToCoach(client);
Get5_Message(client, "%t", "MoveToCoachInfoMessage");
Expand All @@ -110,7 +110,7 @@ public bool CheckIfClientCoaching(int client, MatchTeam team) {
// Force user to join the coach if specified by config or reconnect.
char clientAuth64[AUTH_LENGTH];
GetAuth(client, clientAuth64, AUTH_LENGTH);
if (g_CoachingEnabledCvar.BoolValue && strcmp(clientAuth64, g_TeamCoaches[team]) == 0) {
if (g_CoachingEnabledCvar.BoolValue && IsAuthOnTeamCoach(clientAuth64, team)) {
LogDebug("Forcing player %N to coach as they were previously.", client);
MoveClientToCoach(client);
return true;
Expand Down Expand Up @@ -139,8 +139,13 @@ public void MoveClientToCoach(int client) {
}

char teamString[4];
char clientAuth[64];
CSTeamString(csTeam, teamString, sizeof(teamString));
GetAuth(client, g_TeamCoaches[matchTeam], AUTH_LENGTH);
GetAuth(client, clientAuth, AUTH_LENGTH);
if (!IsAuthOnTeamCoach(clientAuth, matchTeam)) {
AddCoachToTeam(clientAuth, matchTeam, "");
}

// If we're in warmup we use the in-game
// coaching command. Otherwise we manually move them to spec
// and set the coaching target.
Expand Down Expand Up @@ -171,9 +176,8 @@ public Action Command_SmCoach(int client, int args) {

GetAuth(client, auth, sizeof(auth));
MatchTeam matchTeam = GetClientMatchTeam(client);
// Don't allow a new coach if one is already selected.
LogDebug("Attempting to get auth %s to coach when coach is %s", auth, g_TeamCoaches[matchTeam]);
if (!StrEqual(g_TeamCoaches[matchTeam], auth)) {
// Don't allow a new coach if spots are full.
if (GetTeamCoaches(matchTeam).Length > g_CoachesPerTeam) {
return Plugin_Stop;
}

Expand Down Expand Up @@ -263,6 +267,24 @@ public MatchTeam GetAuthMatchTeam(const char[] steam64) {
return MatchTeam_TeamNone;
}

public MatchTeam GetAuthMatchTeamCoach(const char[] steam64) {
if (g_GameState == Get5State_None) {
return MatchTeam_TeamNone;
}

if (g_InScrimMode) {
return IsAuthOnTeamCoach(steam64, MatchTeam_Team1) ? MatchTeam_Team1 : MatchTeam_Team2;
}

for (int i = 0; i < MATCHTEAM_COUNT; i++) {
MatchTeam team = view_as<MatchTeam>(i);
if (IsAuthOnTeamCoach(steam64, team)) {
return team;
}
}
return MatchTeam_TeamNone;
}

stock int CountPlayersOnCSTeam(int team, int exclude = -1) {
int count = 0;
for (int i = 1; i <= MaxClients; i++) {
Expand Down Expand Up @@ -334,10 +356,18 @@ public ArrayList GetTeamAuths(MatchTeam team) {
return g_TeamAuths[team];
}

public ArrayList GetTeamCoaches(MatchTeam team) {
return g_TeamCoaches[team];
}

public bool IsAuthOnTeam(const char[] auth, MatchTeam team) {
return GetTeamAuths(team).FindString(auth) >= 0;
}

public bool IsAuthOnTeamCoach(const char[] auth, MatchTeam team) {
return GetTeamCoaches(team).FindString(auth) >= 0;
}

public void SetStartingTeams() {
int mapNumber = GetMapNumber();
if (mapNumber >= g_MapSides.Length || g_MapSides.Get(mapNumber) == SideChoice_KnifeRound) {
Expand Down Expand Up @@ -394,6 +424,23 @@ public bool AddPlayerToTeam(const char[] auth, MatchTeam team, const char[] name
}
}

public bool AddCoachToTeam(const char[] auth, MatchTeam team, const char[] name) {
char steam64[AUTH_LENGTH];
ConvertAuthToSteam64(auth, steam64);

if (team == MatchTeam_TeamSpec) {
LogDebug("Not allowed to coach a spectator team.");
return false;
}
if (GetAuthMatchTeamCoach(steam64) == MatchTeam_TeamNone) {
GetTeamCoaches(team).PushString(steam64);
Get5_SetPlayerName(auth, name);
return true;
} else {
return false;
}
}

public bool RemovePlayerFromTeams(const char[] auth) {
char steam64[AUTH_LENGTH];
ConvertAuthToSteam64(auth, steam64);
Expand Down

0 comments on commit a0ad3f8

Please sign in to comment.