a0b8153f4b
Explanation: There are two clients in the server, one named gene, the other one "Ene ~special characters~". An admin issues "sm_slay Ene" and gets following error message: More than one client matched the given pattern. What this hack will do is: Use GetCmdArg(0, ...); to get the command name "sm_slay". Use GetCmdArgString(...); to get the arguments supplied to the command. Use GetLastProcessTargetString(...); (which was implemented in this commit) to retrieve the arguments that were passed to the last ProcessTargetString call. It will then pass this data to the DynamicTargeting plugin through its AmbiguousMenu native. The plugin will open up a menu on the client and list all targets which match the pattern that was supplied to ProcessTargetString. If the client selects a menu entry, FakeClientCommand will be used to re-execute the command with the correct target.
267 lines
6.3 KiB
SourcePawn
267 lines
6.3 KiB
SourcePawn
#pragma semicolon 1
|
|
#define PLUGIN_VERSION "1.0"
|
|
|
|
#include <sourcemod>
|
|
#include <DynamicTargeting>
|
|
|
|
#pragma newdecls required
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "Dynamic Targeting",
|
|
author = "BotoX",
|
|
description = "",
|
|
version = PLUGIN_VERSION,
|
|
url = ""
|
|
}
|
|
|
|
char g_PlayerNames[MAXPLAYERS + 1][MAX_NAME_LENGTH];
|
|
Handle g_PlayerData[MAXPLAYERS + 1];
|
|
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
CreateNative("AmbiguousMenu", Native_AmbiguousMenu);
|
|
RegPluginLibrary("DynamicTargeting");
|
|
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
if(g_PlayerData[client] != INVALID_HANDLE)
|
|
{
|
|
CloseHandle(g_PlayerData[client]);
|
|
g_PlayerData[client] = INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
int CreateAmbiguousMenu(int client, const char[] sCommand, const char[] sArgString, const char[] sPattern, int FilterFlags)
|
|
{
|
|
Menu menu = new Menu(MenuHandler_AmbiguousMenu, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem);
|
|
menu.ExitButton = true;
|
|
|
|
char sTitle[32 + MAX_TARGET_LENGTH];
|
|
FormatEx(sTitle, sizeof(sTitle), "Target \"%s\" is ambiguous.", sPattern);
|
|
menu.SetTitle(sTitle);
|
|
|
|
int Players = 0;
|
|
int[] aClients = new int[MaxClients + 1];
|
|
|
|
for(int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if(!IsClientConnected(i) || i == client)
|
|
continue;
|
|
|
|
if(FilterFlags & COMMAND_FILTER_NO_BOTS && IsFakeClient(i))
|
|
continue;
|
|
|
|
if(!(FilterFlags & COMMAND_FILTER_CONNECTED) && !IsClientInGame(i))
|
|
continue;
|
|
|
|
if(FilterFlags & COMMAND_FILTER_ALIVE && !IsPlayerAlive(i))
|
|
continue;
|
|
|
|
if(FilterFlags & COMMAND_FILTER_DEAD && IsPlayerAlive(i))
|
|
continue;
|
|
|
|
// insert player names into g_PlayerNames array
|
|
GetClientName(i, g_PlayerNames[i], sizeof(g_PlayerNames[]));
|
|
|
|
if(StrContains(g_PlayerNames[i], sPattern, false) != -1)
|
|
aClients[Players++] = i;
|
|
}
|
|
|
|
// sort aClients array by player name
|
|
SortCustom1D(aClients, Players, SortByPlayerName);
|
|
|
|
// insert players sorted
|
|
char sUserId[12];
|
|
char sDisp[MAX_NAME_LENGTH + 16];
|
|
for(int i = 0; i < Players; i++)
|
|
{
|
|
IntToString(GetClientUserId(aClients[i]), sUserId, sizeof(sUserId));
|
|
|
|
FormatEx(sDisp, sizeof(sDisp), "%s (%s)", g_PlayerNames[aClients[i]], sUserId);
|
|
menu.AddItem(sUserId, sDisp);
|
|
}
|
|
|
|
DataPack pack = new DataPack();
|
|
pack.WriteString(sCommand);
|
|
pack.WriteString(sArgString);
|
|
pack.WriteString(sPattern);
|
|
pack.WriteCell(FilterFlags);
|
|
|
|
if(g_PlayerData[client] != INVALID_HANDLE)
|
|
{
|
|
CloseHandle(g_PlayerData[client]);
|
|
g_PlayerData[client] = INVALID_HANDLE;
|
|
}
|
|
CancelClientMenu(client);
|
|
|
|
g_PlayerData[client] = pack;
|
|
menu.Display(client, MENU_TIME_FOREVER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public int MenuHandler_AmbiguousMenu(Menu menu, MenuAction action, int param1, int param2)
|
|
{
|
|
switch(action)
|
|
{
|
|
case MenuAction_End:
|
|
{
|
|
CloseHandle(menu);
|
|
}
|
|
case MenuAction_Cancel:
|
|
{
|
|
if(g_PlayerData[param1] != INVALID_HANDLE)
|
|
{
|
|
CloseHandle(g_PlayerData[param1]);
|
|
g_PlayerData[param1] = INVALID_HANDLE;
|
|
}
|
|
}
|
|
case MenuAction_Select:
|
|
{
|
|
int Style;
|
|
char sItem[32];
|
|
char sDisp[MAX_NAME_LENGTH + 16];
|
|
menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));
|
|
|
|
int UserId = StringToInt(sItem);
|
|
int client = GetClientOfUserId(UserId);
|
|
if(!client)
|
|
{
|
|
PrintToChat(param1, "\x04[DynamicTargeting]\x01 Player no longer available.");
|
|
menu.DisplayAt(param1, GetMenuSelectionPosition(), MENU_TIME_FOREVER);
|
|
return 0;
|
|
}
|
|
|
|
DataPack pack = view_as<DataPack>(g_PlayerData[param1]);
|
|
pack.Reset();
|
|
|
|
char sCommand[128];
|
|
pack.ReadString(sCommand, sizeof(sCommand));
|
|
|
|
char sArgString[256];
|
|
pack.ReadString(sArgString, sizeof(sArgString));
|
|
|
|
char sPattern[MAX_TARGET_LENGTH];
|
|
pack.ReadString(sPattern, sizeof(sPattern));
|
|
|
|
int Result = ReCallAmbiguous(param1, client, sCommand, sArgString, sPattern);
|
|
|
|
return Result;
|
|
}
|
|
case MenuAction_DrawItem:
|
|
{
|
|
int Style;
|
|
char sItem[32];
|
|
menu.GetItem(param2, sItem, sizeof(sItem), Style);
|
|
|
|
int UserId = StringToInt(sItem);
|
|
int client = GetClientOfUserId(UserId);
|
|
if(!client) // Player disconnected
|
|
return ITEMDRAW_DISABLED;
|
|
|
|
return Style;
|
|
}
|
|
case MenuAction_DisplayItem:
|
|
{
|
|
int Style;
|
|
char sItem[32];
|
|
char sDisp[MAX_NAME_LENGTH + 16];
|
|
menu.GetItem(param2, sItem, sizeof(sItem), Style, sDisp, sizeof(sDisp));
|
|
|
|
if(!sItem[0])
|
|
return 0;
|
|
|
|
char sBuffer[MAX_NAME_LENGTH + 16];
|
|
int UserId = StringToInt(sItem);
|
|
int client = GetClientOfUserId(UserId);
|
|
if(!client) // Player disconnected
|
|
return 0;
|
|
|
|
GetClientName(client, g_PlayerNames[client], sizeof(g_PlayerNames[]));
|
|
FormatEx(sBuffer, sizeof(sBuffer), "%s (%d)", g_PlayerNames[client], UserId);
|
|
|
|
if(!StrEqual(sDisp, sBuffer))
|
|
return RedrawMenuItem(sBuffer);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ReCallAmbiguous(int client, int newClient, const char[] sCommand, const char[] sArgString, const char[] sPattern)
|
|
{
|
|
char sTarget[16];
|
|
FormatEx(sTarget, sizeof(sTarget), "#%d", GetClientUserId(newClient));
|
|
|
|
char sNewArgString[256];
|
|
strcopy(sNewArgString, sizeof(sNewArgString), sArgString);
|
|
|
|
char sPart[256];
|
|
int CurrentIndex = 0;
|
|
int NextIndex = 0;
|
|
|
|
while(NextIndex != -1 && CurrentIndex < sizeof(sNewArgString))
|
|
{
|
|
NextIndex = BreakString(sNewArgString[CurrentIndex], sPart, sizeof(sPart));
|
|
|
|
if(StrEqual(sPart, sPattern))
|
|
{
|
|
ReplaceStringEx(sNewArgString[CurrentIndex], sizeof(sNewArgString) - CurrentIndex, sPart, sTarget);
|
|
break;
|
|
}
|
|
|
|
CurrentIndex += NextIndex;
|
|
}
|
|
|
|
FakeClientCommandEx(client, "%s %s", sCommand, sNewArgString);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public int Native_AmbiguousMenu(Handle plugin, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
if(client > MaxClients || client <= 0)
|
|
{
|
|
ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid.");
|
|
return -1;
|
|
}
|
|
|
|
if(!IsClientInGame(client))
|
|
{
|
|
ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game.");
|
|
return -1;
|
|
}
|
|
|
|
if(IsFakeClient(client))
|
|
{
|
|
ThrowNativeError(SP_ERROR_NATIVE, "Client is fake-client.");
|
|
return -1;
|
|
}
|
|
|
|
char sCommand[128];
|
|
GetNativeString(2, sCommand, sizeof(sCommand));
|
|
|
|
char sArgString[256];
|
|
GetNativeString(3, sArgString, sizeof(sArgString));
|
|
|
|
char sPattern[MAX_TARGET_LENGTH];
|
|
GetNativeString(4, sPattern, sizeof(sPattern));
|
|
|
|
int FilterFlags = GetNativeCell(5);
|
|
|
|
return CreateAmbiguousMenu(client, sCommand, sArgString, sPattern, FilterFlags);
|
|
}
|
|
|
|
public int SortByPlayerName(int elem1, int elem2, const int[] array, Handle hndl)
|
|
{
|
|
return strcmp(g_PlayerNames[elem1], g_PlayerNames[elem2], false);
|
|
}
|