diff --git a/core/logic/smn_players.cpp b/core/logic/smn_players.cpp
index 17e2262f15..b3cc95e733 100644
--- a/core/logic/smn_players.cpp
+++ b/core/logic/smn_players.cpp
@@ -1458,24 +1458,23 @@ static cell_t IsClientInKickQueue(IPluginContext *pContext, const cell_t *params
return pPlayer->IsInKickQueue() ? 1 : 0;
}
+cmd_target_info_t g_ProcessTargetString_info;
static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params)
{
- cmd_target_info_t info;
-
- pContext->LocalToString(params[1], (char **) &info.pattern);
- info.admin = params[2];
- pContext->LocalToPhysAddr(params[3], &info.targets);
- info.max_targets = params[4];
- info.flags = params[5];
- pContext->LocalToString(params[6], &info.target_name);
- info.target_name_maxlength = params[7];
+ pContext->LocalToString(params[1], (char **) &g_ProcessTargetString_info.pattern);
+ g_ProcessTargetString_info.admin = params[2];
+ pContext->LocalToPhysAddr(params[3], &g_ProcessTargetString_info.targets);
+ g_ProcessTargetString_info.max_targets = params[4];
+ g_ProcessTargetString_info.flags = params[5];
+ pContext->LocalToString(params[6], &g_ProcessTargetString_info.target_name);
+ g_ProcessTargetString_info.target_name_maxlength = params[7];
cell_t *tn_is_ml;
pContext->LocalToPhysAddr(params[8], &tn_is_ml);
- playerhelpers->ProcessCommandTarget(&info);
+ playerhelpers->ProcessCommandTarget(&g_ProcessTargetString_info);
- if (info.target_name_style == COMMAND_TARGETNAME_ML)
+ if (g_ProcessTargetString_info.target_name_style == COMMAND_TARGETNAME_ML)
{
*tn_is_ml = 1;
}
@@ -1484,16 +1483,30 @@ static cell_t ProcessTargetString(IPluginContext *pContext, const cell_t *params
*tn_is_ml = 0;
}
- if (info.num_targets == 0)
+ if (g_ProcessTargetString_info.num_targets == 0)
{
- return info.reason;
+ return g_ProcessTargetString_info.reason;
}
else
{
- return info.num_targets;
+ return g_ProcessTargetString_info.num_targets;
}
}
+static cell_t GetLastProcessTargetString(IPluginContext *pContext, const cell_t *params)
+{
+ cell_t *admin, *flags;
+
+ pContext->StringToLocalUTF8(params[1], params[2], g_ProcessTargetString_info.pattern, NULL);
+ pContext->LocalToPhysAddr(params[3], &admin);
+ pContext->LocalToPhysAddr(params[4], &flags);
+
+ *admin = g_ProcessTargetString_info.admin;
+ *flags = g_ProcessTargetString_info.flags;
+
+ return 0;
+}
+
static cell_t FormatActivitySource(IPluginContext *pContext, const cell_t *params)
{
int value;
@@ -1645,6 +1658,7 @@ REGISTER_NATIVES(playernatives)
{ "NotifyPostAdminCheck", NotifyPostAdminCheck },
{ "IsClientInKickQueue", IsClientInKickQueue },
{ "ProcessTargetString", ProcessTargetString },
+ { "GetLastProcessTargetString", GetLastProcessTargetString },
{ "FormatActivitySource", FormatActivitySource },
{ "GetClientSerial", sm_GetClientSerial },
{ "GetClientFromSerial", sm_GetClientFromSerial },
diff --git a/core/smn_console.cpp b/core/smn_console.cpp
index fbb6f206ff..da9c12f4bb 100644
--- a/core/smn_console.cpp
+++ b/core/smn_console.cpp
@@ -795,6 +795,16 @@ static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
return 1;
}
+static cell_t sm_IsCommandCallback(IPluginContext *pContext, const cell_t *params)
+{
+ const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
+
+ if (!pCmd)
+ return 0;
+
+ return 1;
+}
+
static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
{
const ICommandArgs *pCmd = g_HL2.PeekCommandStack();
@@ -1473,6 +1483,7 @@ REGISTER_NATIVES(consoleNatives)
{"GetConVarDefault", GetConVarDefault},
{"RegServerCmd", sm_RegServerCmd},
{"RegConsoleCmd", sm_RegConsoleCmd},
+ {"IsCommandCallback", sm_IsCommandCallback},
{"GetCmdArgString", sm_GetCmdArgString},
{"GetCmdArgs", sm_GetCmdArgs},
{"GetCmdArg", sm_GetCmdArg},
diff --git a/plugins/AMBuilder b/plugins/AMBuilder
index bf9c34903d..35d50df5b0 100644
--- a/plugins/AMBuilder
+++ b/plugins/AMBuilder
@@ -24,7 +24,8 @@ files = [
'basecommands.sp',
'mapchooser.sp',
'randomcycle.sp',
- 'sql-admin-manager.sp'
+ 'sql-admin-manager.sp',
+ 'DynamicTargeting.sp'
]
spcomp_argv = [
diff --git a/plugins/DynamicTargeting.sp b/plugins/DynamicTargeting.sp
new file mode 100644
index 0000000000..4719c5e95d
--- /dev/null
+++ b/plugins/DynamicTargeting.sp
@@ -0,0 +1,298 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Dynamic Targeting Plugin
+ * Builds a menu of ambiguous targets and re-executes command on selection.
+ *
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#pragma semicolon 1
+
+#include
+#include
+
+#pragma newdecls required
+
+public Plugin myinfo =
+{
+ name = "Dynamic Targeting",
+ author = "AlliedModders LLC",
+ description = "Dynamic targeting menu on ambiguous match.",
+ version = SOURCEMOD_VERSION,
+ url = "http://www.sourcemod.net/"
+}
+
+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(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);
+}
diff --git a/plugins/include/DynamicTargeting.inc b/plugins/include/DynamicTargeting.inc
new file mode 100644
index 0000000000..81da0aec24
--- /dev/null
+++ b/plugins/include/DynamicTargeting.inc
@@ -0,0 +1,56 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This file is part of the SourceMod/SourcePawn SDK.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#if defined _DynamicTargeting_Included
+ #endinput
+#endif
+#define _DynamicTargeting_Included
+
+native int AmbiguousMenu(int client, char[] sCommand, char[] sArgString, char[] sPattern, int FilterFlags);
+
+public SharedPlugin __pl_DynamicTargeting =
+{
+ name = "DynamicTargeting",
+ file = "DynamicTargeting.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+#if !defined REQUIRE_PLUGIN
+public __pl_DynamicTargeting_SetNTVOptional()
+{
+ MarkNativeAsOptional("AmbiguousMenu");
+}
+#endif
diff --git a/plugins/include/commandfilters.inc b/plugins/include/commandfilters.inc
index 4b324a33c5..5020ca1c72 100644
--- a/plugins/include/commandfilters.inc
+++ b/plugins/include/commandfilters.inc
@@ -84,6 +84,25 @@ native int ProcessTargetString(const char[] pattern,
int tn_maxlength,
bool &tn_is_ml);
+
+/**
+ * Retrieves arguments that were passed to the last ProcessTargetString call.
+ *
+ * @param pattern Buffer to store the pattern.
+ * @param p_maxlen Maximum length of the pattern buffer.
+ * @param admin OUTPUT: Admin performing the action, or 0 if the server.
+ * @param filter_flags OUTPUT: Filter flags.
+ * @noreturn
+ */
+native void GetLastProcessTargetString(char[] pattern,
+ int p_maxlen,
+ int &admin,
+ int &filter_flags);
+
+#undef REQUIRE_PLUGIN
+#include
+#define REQUIRE_PLUGIN
+
/**
* Replies to a client with a given message describing a targetting
* failure reason.
@@ -93,7 +112,7 @@ native int ProcessTargetString(const char[] pattern,
* @param client Client index, or 0 for server.
* @param reason COMMAND_TARGET reason.
*/
-stock void ReplyToTargetError(int client, int reason)
+stock void ReplyToTargetError(int client, int reason, bool dynamic=true)
{
switch (reason)
{
@@ -128,6 +147,34 @@ stock void ReplyToTargetError(int client, int reason)
case COMMAND_TARGET_AMBIGUOUS:
{
ReplyToCommand(client, "[SM] %t", "More than one client matched");
+
+ if(dynamic &&
+ GetFeatureStatus(FeatureType_Native, "GetLastProcessTargetString") == FeatureStatus_Available &&
+ LibraryExists("DynamicTargeting"))
+ {
+ if(GetFeatureStatus(FeatureType_Native, "IsCommandCallback") == FeatureStatus_Available &&
+ !IsCommandCallback())
+ {
+ return;
+ }
+
+ char sCommand[128];
+ GetCmdArg(0, sCommand, sizeof(sCommand));
+
+ char sArgString[256];
+ GetCmdArgString(sArgString, sizeof(sArgString));
+
+ char pattern[MAX_TARGET_LENGTH];
+ int admin;
+ int filter_flags;
+
+ GetLastProcessTargetString(pattern, sizeof(pattern), admin, filter_flags);
+
+ if(!admin || !IsClientInGame(admin) || IsFakeClient(admin))
+ return;
+
+ AmbiguousMenu(admin, sCommand, sArgString, pattern, filter_flags);
+ }
}
}
}
diff --git a/plugins/include/console.inc b/plugins/include/console.inc
index c08738ca74..1ab0481206 100644
--- a/plugins/include/console.inc
+++ b/plugins/include/console.inc
@@ -402,7 +402,14 @@ native void RegAdminCmd(const char[] cmd,
const char[] description="",
const char[] group="",
int flags=0);
-
+
+/**
+ * Returns whether there is a command callback available.
+ *
+ * @return True if called from inside a command callback.
+ */
+native bool IsCommandCallback();
+
/**
* Returns the number of arguments from the current console or server command.
* @note Unlike the HL2 engine call, this does not include the command itself.
diff --git a/plugins/include/core.inc b/plugins/include/core.inc
index b6aaf6e145..bc1377cc50 100644
--- a/plugins/include/core.inc
+++ b/plugins/include/core.inc
@@ -311,6 +311,9 @@ public void __ext_core_SetNTVOptional()
MarkNativeAsOptional("Protobuf.ReadRepeatedMessage");
MarkNativeAsOptional("Protobuf.AddMessage");
+ MarkNativeAsOptional("IsCommandCallback");
+ MarkNativeAsOptional("GetLastProcessTargetString");
+
VerifyCoreVersion();
}
diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc
index 3d26176806..57598570ce 100644
--- a/plugins/include/sourcemod.inc
+++ b/plugins/include/sourcemod.inc
@@ -47,6 +47,91 @@ struct Plugin
public const char[] url; /**< Plugin URL */
};
+/**
+ * Returns whether a library exists. This function should be considered
+ * expensive; it should only be called on plugin to determine availability
+ * of resources. Use OnLibraryAdded()/OnLibraryRemoved() to detect changes
+ * in libraries.
+ *
+ * @param name Library name of a plugin or extension.
+ * @return True if exists, false otherwise.
+ */
+native bool LibraryExists(const char[] name);
+
+/**
+ * Feature types.
+ */
+enum FeatureType
+{
+ /**
+ * A native function call.
+ */
+ FeatureType_Native,
+
+ /**
+ * A named capability. This is distinctly different from checking for a
+ * native, because the underlying functionality could be enabled on-demand
+ * to improve loading time. Thus a native may appear to exist, but it might
+ * be part of a set of features that are not compatible with the current game
+ * or version of SourceMod.
+ */
+ FeatureType_Capability
+};
+
+/**
+ * Feature statuses.
+ */
+enum FeatureStatus
+{
+ /**
+ * Feature is available for use.
+ */
+ FeatureStatus_Available,
+
+ /**
+ * Feature is not available.
+ */
+ FeatureStatus_Unavailable,
+
+ /**
+ * Feature is not known at all.
+ */
+ FeatureStatus_Unknown
+};
+
+/**
+ * Returns whether "GetFeatureStatus" will work. Using this native
+ * or this function will not cause SourceMod to fail loading on older versions,
+ * however, GetFeatureStatus will only work if this function returns true.
+ *
+ * @return True if GetFeatureStatus will work, false otherwise.
+ */
+stock bool CanTestFeatures()
+{
+ return LibraryExists("__CanTestFeatures__");
+}
+
+/**
+ * Returns whether a feature exists, and if so, whether it is usable.
+ *
+ * @param type Feature type.
+ * @param name Feature name.
+ * @return Feature status.
+ */
+native FeatureStatus GetFeatureStatus(FeatureType type, const char[] name);
+
+/**
+ * Requires that a given feature is available. If it is not, SetFailState()
+ * is called with the given message.
+ *
+ * @param type Feature type.
+ * @param name Feature name.
+ * @param fmt Message format string, or empty to use default.
+ * @param ... Message format parameters, if any.
+ */
+native void RequireFeature(FeatureType type, const char[] name,
+ const char[] fmt="", any ...);
+
#include
#include
#include
@@ -448,17 +533,6 @@ native void AutoExecConfig(bool autoCreate=true, const char[] name="", const cha
*/
native void RegPluginLibrary(const char[] name);
-/**
- * Returns whether a library exists. This function should be considered
- * expensive; it should only be called on plugin to determine availability
- * of resources. Use OnLibraryAdded()/OnLibraryRemoved() to detect changes
- * in libraries.
- *
- * @param name Library name of a plugin or extension.
- * @return True if exists, false otherwise.
- */
-native bool LibraryExists(const char[] name);
-
/**
* Returns the status of an extension, by filename.
*
@@ -582,80 +656,6 @@ forward bool OnClientFloodCheck(int client);
*/
forward void OnClientFloodResult(int client, bool blocked);
-/**
- * Feature types.
- */
-enum FeatureType
-{
- /**
- * A native function call.
- */
- FeatureType_Native,
-
- /**
- * A named capability. This is distinctly different from checking for a
- * native, because the underlying functionality could be enabled on-demand
- * to improve loading time. Thus a native may appear to exist, but it might
- * be part of a set of features that are not compatible with the current game
- * or version of SourceMod.
- */
- FeatureType_Capability
-};
-
-/**
- * Feature statuses.
- */
-enum FeatureStatus
-{
- /**
- * Feature is available for use.
- */
- FeatureStatus_Available,
-
- /**
- * Feature is not available.
- */
- FeatureStatus_Unavailable,
-
- /**
- * Feature is not known at all.
- */
- FeatureStatus_Unknown
-};
-
-/**
- * Returns whether "GetFeatureStatus" will work. Using this native
- * or this function will not cause SourceMod to fail loading on older versions,
- * however, GetFeatureStatus will only work if this function returns true.
- *
- * @return True if GetFeatureStatus will work, false otherwise.
- */
-stock bool CanTestFeatures()
-{
- return LibraryExists("__CanTestFeatures__");
-}
-
-/**
- * Returns whether a feature exists, and if so, whether it is usable.
- *
- * @param type Feature type.
- * @param name Feature name.
- * @return Feature status.
- */
-native FeatureStatus GetFeatureStatus(FeatureType type, const char[] name);
-
-/**
- * Requires that a given feature is available. If it is not, SetFailState()
- * is called with the given message.
- *
- * @param type Feature type.
- * @param name Feature name.
- * @param fmt Message format string, or empty to use default.
- * @param ... Message format parameters, if any.
- */
-native void RequireFeature(FeatureType type, const char[] name,
- const char[] fmt="", any ...);
-
/**
* Represents how many bytes we can read from an address with one load
*/
diff --git a/tools/buildbot/PackageScript b/tools/buildbot/PackageScript
index 88a075e3a8..2f1765d60f 100644
--- a/tools/buildbot/PackageScript
+++ b/tools/buildbot/PackageScript
@@ -324,6 +324,7 @@ CopyFiles('plugins', 'addons/sourcemod/scripting',
'rockthevote.sp',
'sounds.sp',
'sql-admin-manager.sp',
+ 'DynamicTargeting.sp',
]
)
CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
@@ -394,6 +395,7 @@ CopyFiles('plugins/include', 'addons/sourcemod/scripting/include',
'usermessages.inc',
'vector.inc',
'version.inc',
+ 'DynamicTargeting.inc',
]
)
CopyFiles('translations', 'addons/sourcemod/translations',