diff --git a/core/HalfLife2.cpp b/core/HalfLife2.cpp index 61b1dc5e..634329dd 100644 --- a/core/HalfLife2.cpp +++ b/core/HalfLife2.cpp @@ -85,6 +85,8 @@ void CHalfLife2::OnSourceModStartup(bool late) void CHalfLife2::OnSourceModAllInitialized() { m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg"); + m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText"); + m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu"); g_ShareSys.AddInterface(NULL, this); } @@ -260,3 +262,57 @@ bool CHalfLife2::TextMsg(int client, int dest, const char *msg) return true; } + +bool CHalfLife2::HinTextMsg(int client, const char *msg) +{ + bf_write *pBitBuf = NULL; + cell_t players[] = {client}; + + if ((pBitBuf = g_UserMsgs.StartMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL) + { + return false; + } + + pBitBuf->WriteByte(1); + pBitBuf->WriteString(msg); + g_UserMsgs.EndMessage(); + + return true; +} + +bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show) +{ + bf_write *pBitBuf = NULL; + KeyValues *SubKey = NULL; + int count = 0; + cell_t players[] = {client}; + + if ((pBitBuf = g_UserMsgs.StartMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL) + { + return false; + } + + if (data) + { + SubKey = data->GetFirstSubKey(); + while (SubKey) + { + count++; + SubKey = SubKey->GetNextKey(); + } + SubKey = data->GetFirstSubKey(); + } + + pBitBuf->WriteString(name); + pBitBuf->WriteByte((show) ? 1 : 0); + pBitBuf->WriteByte(count); + while (SubKey) + { + pBitBuf->WriteString(SubKey->GetName()); + pBitBuf->WriteString(SubKey->GetString()); + SubKey = SubKey->GetNextKey(); + } + g_UserMsgs.EndMessage(); + + return true; +} diff --git a/core/HalfLife2.h b/core/HalfLife2.h index 2b0bc92b..6273d190 100644 --- a/core/HalfLife2.h +++ b/core/HalfLife2.h @@ -20,6 +20,7 @@ #include "sm_trie.h" #include "sm_globals.h" #include +#include using namespace SourceHook; using namespace SourceMod; @@ -57,6 +58,8 @@ public: //IGameHelpers typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset); void SetEdictStateChanged(edict_t *pEdict, unsigned short offset); bool TextMsg(int client, int dest, const char *msg); + bool HinTextMsg(int client, const char *msg); + bool ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show); private: DataTableInfo *_FindServerClass(const char *classname); private: @@ -64,6 +67,8 @@ private: List m_Tables; THash m_Maps; int m_MsgTextMsg; + int m_HinTextMsg; + int m_VGUIMenu; }; extern CHalfLife2 g_HL2; diff --git a/core/smn_halflife.cpp b/core/smn_halflife.cpp index d1b2b7b9..69f0b1ee 100644 --- a/core/smn_halflife.cpp +++ b/core/smn_halflife.cpp @@ -326,6 +326,78 @@ static cell_t PrintCenterText(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t PrintHintText(IPluginContext *pContext, const cell_t *params) +{ + int client = params[1]; + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } + + if (!pPlayer->IsInGame()) + { + return pContext->ThrowNativeError("Client %d is not in game", client); + } + + g_SourceMod.SetGlobalTarget(client); + + char buffer[192]; + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + + /* Check for an error before printing to the client */ + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + if (!g_HL2.HinTextMsg(client, buffer)) + { + return pContext->ThrowNativeError("Could not send a usermessage"); + } + + return 1; +} + +static cell_t ShowVGUIPanel(IPluginContext *pContext, const cell_t *params) +{ + HandleError herr; + char *name; + KeyValues *pKV = NULL; + int client = params[1]; + Handle_t hndl = static_cast(params[3]); + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } + + if (!pPlayer->IsInGame()) + { + return pContext->ThrowNativeError("Client %d is not in game", client); + } + + if (hndl != BAD_HANDLE) + { + pKV = g_SourceMod.ReadKeyValuesHandle(hndl, &herr, true); + if (herr != HandleError_None) + { + return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr); + } + } + + pContext->LocalToString(params[2], &name); + + if (!g_HL2.ShowVGUIMenu(client, name, pKV, (params[4]) ? true : false)) + { + return pContext->ThrowNativeError("Could not send a usermessage"); + } + + return 1; +} + static HalfLifeNatives s_HalfLifeNatives; REGISTER_NATIVES(halflifeNatives) @@ -354,5 +426,7 @@ REGISTER_NATIVES(halflifeNatives) {"CreateDialog", smn_CreateDialog}, {"PrintToChat", PrintToChat}, {"PrintCenterText", PrintCenterText}, + {"PrintHintText", PrintHintText}, + {"ShowVGUIPanel", ShowVGUIPanel}, {NULL, NULL}, }; diff --git a/plugins/include/halflife.inc b/plugins/include/halflife.inc index fce6439d..85bf862c 100644 --- a/plugins/include/halflife.inc +++ b/plugins/include/halflife.inc @@ -18,6 +18,11 @@ #endif #define _halflife_included +#define MOTDPANEL_TYPE_TEXT 0 /**< Treat msg as plain text */ +#define MOTDPANEL_TYPE_INDEX 1 /**< Msg is auto determined by the engine */ +#define MOTDPANEL_TYPE_URL 2 /**< Treat msg as an URL link */ +#define MOTDPANEL_TYPE_FILE 3 /**< Treat msg as a filename to be openned */ + enum DialogType { DialogType_Msg = 0, /**< just an on screen message */ @@ -275,3 +280,73 @@ stock PrintCenterTextAll(const String:format[], any:...) } } } + +/** + * Prints a message to a specific client with a hint box. + * + * @param client Client index. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native PrintHintText(client, const String:format[], any:...); + +/** + * Prints a message to all clients with a hint box. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + */ +stock PrintHintTextToAll(const String:format[], any:...) +{ + new maxClients = GetMaxClients(); + decl String:buffer[192]; + + for (new i = 1; i <= maxClients; i++) + { + if (IsClientInGame(i)) + { + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 2); + PrintHintText(i, "%s", buffer); + } + } +} + +/** + * Shows a VGUI panel to a specific client. + * + * @param client Client index. + * @param name Panel type name (Check viewport_panel_names.h to see a list of some panel names). + * @param Kv KeyValues handle with all the data for the panel setup (Depends on the panel type and may be unused). + * @param show True to show the panel, or false to remove it from the client screen. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +native ShowVGUIPanel(client, const String:name[], Handle:Kv=INVALID_HANDLE, bool:show=true); + +/** + * Shows a MOTD panel to a specific client. + * + * @param client Client index. + * @param title Title of the panel (printed on the top border of the window). + * @param msg Contents of the panel, it can be treated as an url, filename or plain text + * depending on the type parameter (WARNING: msg has to be 192 bytes maximum!) + * @param type Determines the way to treat the message body of the panel. + * @noreturn + * @error If the client is not connected an error will be thrown. + */ +stock ShowMOTDPanel(client, const String:title[], const String:msg[], type=MOTDPANEL_TYPE_INDEX) +{ + decl String:num[3]; + new Handle:Kv = CreateKeyValues("data"); + IntToString(type, num, sizeof(num)); + + KvSetString(Kv, "title", title); + KvSetString(Kv, "type", num); + KvSetString(Kv, "msg", msg); + ShowVGUIPanel(client, "info", Kv); + CloseHandle(Kv); +}