Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assign Tank control using priority queue #781

Merged
merged 2 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified addons/sourcemod/plugins/optional/l4d_tank_control_eq.smx
Binary file not shown.
251 changes: 151 additions & 100 deletions addons/sourcemod/scripting/l4d_tank_control_eq.sp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define IS_VALID_SPECTATOR(%1) (IsClientInGame(%1) && IS_SPECTATOR(%1))

ArrayList h_whosHadTank;
ArrayList h_tankQueue;

ConVar
hTankPrint,
Expand Down Expand Up @@ -72,7 +73,8 @@ public void OnPluginStart()

// Initialise the tank arrays/data values
h_whosHadTank = new ArrayList(ByteCountToCells(64));

h_tankQueue = new ArrayList(ByteCountToCells(64));

// Admin commands
RegAdminCmd("sm_tankshuffle", TankShuffle_Cmd, ADMFLAG_SLAY, "Re-picks at random someone to become tank.");
RegAdminCmd("sm_givetank", GiveTank_Cmd, ADMFLAG_SLAY, "Gives the tank to a selected player");
Expand Down Expand Up @@ -150,14 +152,20 @@ public Action L4D_OnTryOfferingTankBot(int tank_index, bool &enterStatis)
strcopy(queuedTankSteamId, sizeof(queuedTankSteamId), sOverrideTank);

// If we don't have a queued tank, choose one
if (strcmp(queuedTankSteamId, "") == 0)
if (StrEqual(queuedTankSteamId, ""))
chooseTank(0);

// Mark the player as having had tank
if (strcmp(queuedTankSteamId, "") != 0)
if (!StrEqual(queuedTankSteamId, ""))
{
setTankTickets(queuedTankSteamId, 20000);
h_whosHadTank.PushString(queuedTankSteamId);

if (h_whosHadTank.FindString(queuedTankSteamId) == -1)
h_whosHadTank.PushString(queuedTankSteamId);

int index = h_tankQueue.FindString(queuedTankSteamId);
if (index != -1)
h_tankQueue.Erase(index);
}

return Plugin_Continue;
Expand Down Expand Up @@ -188,6 +196,7 @@ Action newGame(Handle timer)
if (teamAScore == 0 && teamBScore == 0)
{
h_whosHadTank.Clear();
h_tankQueue.Clear();
queuedTankSteamId = "";
tankInitiallyChosen = "";
}
Expand Down Expand Up @@ -251,10 +260,10 @@ void PlayerTeam_Event(Event hEvent, const char[] name, bool dontBroadcast)

GetClientAuthId(client, AuthId_Steam2, tmpSteamId, sizeof(tmpSteamId));

if (strcmp(tankInitiallyChosen, tmpSteamId) == 0)
if (StrEqual(tankInitiallyChosen, tmpSteamId))
initialTankLeft = GetGameTime();

if (strcmp(queuedTankSteamId, tmpSteamId) == 0)
if (StrEqual(queuedTankSteamId, tmpSteamId))
{
RequestFrame(chooseTank, 0);
RequestFrame(outputTankToAll, 0);
Expand All @@ -264,7 +273,7 @@ void PlayerTeam_Event(Event hEvent, const char[] name, bool dontBroadcast)
if (team == TEAM_INFECTED && !IsFakeClient(client) && !StrEqual(tankInitiallyChosen, ""))
{
GetClientAuthId(client, AuthId_Steam2, tmpSteamId, sizeof(tmpSteamId));
if (strcmp(tankInitiallyChosen, tmpSteamId) == 0)
if (StrEqual(tankInitiallyChosen, tmpSteamId))
{
/* Not touching multiple tanks with a ten-foot pole.
Could technically be done though.. TODO? */
Expand All @@ -290,7 +299,7 @@ void PlayerTeam_Event(Event hEvent, const char[] name, bool dontBroadcast)

/**
* Replaces the current tank with the initially chosen Tank.
* Also removes the current Tank from the h_whosHadTank array.
* And requeues the old Tank.
*
* @param deservingTank
* The player to give the Tank to.
Expand All @@ -306,12 +315,29 @@ void ReplaceTank(int deservingTank)

L4D_ReplaceTank(oldTank, deservingTank);

char oldTankSteamId[64];
GetClientAuthId(oldTank, AuthId_Steam2, oldTankSteamId, sizeof(oldTankSteamId));
char steamId[64];

// Requeue the old tank
GetClientAuthId(oldTank, AuthId_Steam2, steamId, sizeof(steamId));
if (h_tankQueue.FindString(steamId) == -1)
{
h_tankQueue.ShiftUp(0);
h_tankQueue.SetString(0, steamId);
}

int index = h_whosHadTank.FindString(oldTankSteamId);
int index = h_whosHadTank.FindString(steamId);
if (index != -1)
h_whosHadTank.Erase(index);

// Remove the deserving tank from the queue if they're in it
GetClientAuthId(deservingTank, AuthId_Steam2, steamId, sizeof(steamId));
index = h_tankQueue.FindString(steamId);
if (index != -1)
h_tankQueue.Erase(index);

index = h_whosHadTank.FindString(steamId);
if (index == -1)
h_whosHadTank.PushString(steamId);
}
else if (hTankDebug.BoolValue)
PrintToConsoleAll("[TC] oldTank: %i and deservingTank: is%s valid", oldTank, IS_INFECTED(deservingTank) ? "" : " NOT VALID");
Expand Down Expand Up @@ -361,7 +387,7 @@ void TankKilled_Event(Event hEvent, const char[] eName, bool dontBroadcast)
Action Tank_Cmd(int client, int args)
{
// Only output if client is in-game and we have a queued tank
if (!IsClientInGame(client) || strcmp(queuedTankSteamId, "") == 0)
if (!IsClientInGame(client) || StrEqual(queuedTankSteamId, ""))
return Plugin_Handled;

int tankClientId = getInfectedPlayerBySteamId(queuedTankSteamId);
Expand Down Expand Up @@ -447,51 +473,41 @@ void chooseTank(any data)
Call_PushStringEx(sOverrideTank, sizeof(sOverrideTank), SM_PARAM_STRING_UTF8, SM_PARAM_COPYBACK);
Call_Finish();

if (StrEqual(sOverrideTank, ""))
if (!StrEqual(sOverrideTank, ""))
{
// Create our pool of players to choose from.
ArrayList infectedPool = new ArrayList(ByteCountToCells(64));
addTeamSteamIdsToArray(infectedPool, TEAM_INFECTED);

// If there is nobody on the infected team, return (otherwise we'd be stuck trying to select forever)
if (infectedPool.Length == 0)
{
delete infectedPool;
return;
}
strcopy(queuedTankSteamId, sizeof(queuedTankSteamId), sOverrideTank);
return;
}

// Remove players who've already had tank from the pool.
removeTanksFromPool(infectedPool, h_whosHadTank);

// If the infected pool is empty, remove infected players from pool
if (infectedPool.Length == 0) // (when nobody on infected ,error)
{
ArrayList infectedTeam = new ArrayList(ByteCountToCells(64));
addTeamSteamIdsToArray(infectedTeam, TEAM_INFECTED);
if (infectedTeam.Length > 1)
{
removeTanksFromPool(h_whosHadTank, infectedTeam);
chooseTank(0);
}
else
queuedTankSteamId = "";

delete infectedTeam;
delete infectedPool;
return;
}

// Select a random person to become tank
int rndIndex = GetRandomInt(0, infectedPool.Length - 1);
infectedPool.GetString(rndIndex, queuedTankSteamId, sizeof(queuedTankSteamId));
queuedTankSteamId = "";

if (StrEqual(tankInitiallyChosen, ""))
strcopy(tankInitiallyChosen, sizeof(tankInitiallyChosen), queuedTankSteamId);
int nextTankIndex = PeekNextTankIndexInTheQueue();

delete infectedPool;
}
else
strcopy(queuedTankSteamId, sizeof(queuedTankSteamId), sOverrideTank);
if (nextTankIndex == -1)
{
EnqueueNewInfectedPlayers();
nextTankIndex = PeekNextTankIndexInTheQueue();
}

if (nextTankIndex == -1)
{
RemoveAllInfectedFrom(h_tankQueue);
RemoveAllInfectedFrom(h_whosHadTank);
EnqueueNewInfectedPlayers();
nextTankIndex = PeekNextTankIndexInTheQueue();
}

if (nextTankIndex == -1)
return;

char steamId[64];

h_tankQueue.GetString(nextTankIndex, steamId, sizeof(steamId));

strcopy(queuedTankSteamId, sizeof(queuedTankSteamId), steamId);

if (StrEqual(tankInitiallyChosen, ""))
strcopy(tankInitiallyChosen, sizeof(tankInitiallyChosen), steamId);
}

/**
Expand Down Expand Up @@ -552,52 +568,6 @@ int getTankPlayer()
return -1;
}

/**
* Adds steam ids for a particular team to an array.
*
* @param steamIds
* The array steam ids will be added to.
* @param team
* The team to get steam ids for.
*/
void addTeamSteamIdsToArray(ArrayList steamIds, int team)
{
char steamId[64];

for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || IsFakeClient(i) || GetClientTeam(i) != team)
continue;

GetClientAuthId(i, AuthId_Steam2, steamId, sizeof(steamId));
steamIds.PushString(steamId);
}
}

/**
* Removes steam ids from the tank pool if they've already had tank.
*
* @param steamIdTankPool
* The array containing potential steam ids to become tank.
* @param tanks
* The array containing steam ids of players who've already had tank.
*/
void removeTanksFromPool(ArrayList steamIdTankPool, ArrayList tanks)
{
int index;
char steamId[64];
int ArraySize = tanks.Length;

for (int i = 0; i < ArraySize; i++)
{
tanks.GetString(i, steamId, sizeof(steamId));
index = steamIdTankPool.FindString(steamId);

if (index != -1)
steamIdTankPool.Erase(index);
}
}

/**
* Retrieves a player's client index by their steam id.
*
Expand All @@ -618,7 +588,7 @@ int getInfectedPlayerBySteamId(const char[] steamId)

GetClientAuthId(i, AuthId_Steam2, tmpSteamId, sizeof(tmpSteamId));

if (strcmp(steamId, tmpSteamId) == 0)
if (StrEqual(steamId, tmpSteamId))
return i;
}

Expand All @@ -645,3 +615,84 @@ CountdownTimer GetFrustrationTimer(int client)

return view_as<CountdownTimer>(GetEntityAddress(client) + view_as<Address>(s_iOffs_m_frustrationTimer));
}

int PeekNextTankIndexInTheQueue()
{
if (h_tankQueue.Length == 0)
return -1;

char steamId[64];

for (int i = 0; i < h_tankQueue.Length; i++)
{
h_tankQueue.GetString(i, steamId, sizeof(steamId));

int client = getInfectedPlayerBySteamId(steamId);
if (client != -1)
return i;
}

return -1;
}

void EnqueueNewInfectedPlayers()
{
char steamId[64];

int start = h_tankQueue.Length;
int end = -1;

for (int client = 1; client <= MaxClients; client++)
{
if (!IsClientInGame(client) || IsFakeClient(client) || GetClientTeam(client) != TEAM_INFECTED)
continue;

GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId));

if (h_tankQueue.FindString(steamId) != -1 || h_whosHadTank.FindString(steamId) != -1)
continue;

h_tankQueue.PushString(steamId);

end = h_tankQueue.Length - 1;
}

if (end != -1)
ShuffleArray(h_tankQueue, start, end);
}

void RemoveAllInfectedFrom(ArrayList arrayList)
{
char steamId[64];

for (int client = 1; client <= MaxClients; client++)
{
if (!IsClientInGame(client) || IsFakeClient(client) || GetClientTeam(client) != TEAM_INFECTED)
continue;

GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId));

int index = arrayList.FindString(steamId);
if (index != -1)
arrayList.Erase(index);
}
}

void ShuffleArray(ArrayList arrayList, int start, int end)
{
if (start == end)
return;

int swaps = (end - start + 1) * 2;

for (int i = 0; i < swaps; i++)
{
int index1 = GetRandomInt(start, end);
int index2 = GetRandomInt(start, end);

if (index1 == index2)
continue;

arrayList.SwapAt(index1, index2);
}
}
Loading