ds@1952: /** ds@1952: * vim: set ts=4 : ds@1952: * ============================================================================= ds@1952: * SourceMod ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1952: * ============================================================================= ds@1952: * ds@1952: * This program is free software; you can redistribute it and/or modify it under ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the ds@1952: * Free Software Foundation. ds@1952: * ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1952: * details. ds@1952: * ds@1952: * You should have received a copy of the GNU General Public License along with ds@1952: * this program. If not, see . ds@1952: * ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1952: * this exception to all derivative works. AlliedModders LLC defines further ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1952: * or . ds@1952: * ds@1952: * Version: $Id$ ds@1952: */ ds@1952: ds@1952: #include "sm_stringutil.h" ds@1952: #include "PlayerManager.h" ds@1952: #include "MenuStyle_Valve.h" ds@1952: #include "Translator.h" ds@1952: #include "PlayerManager.h" ds@1952: #include "ConCmdManager.h" ds@1952: ds@1952: SH_DECL_HOOK4_void(IServerPluginHelpers, CreateMessage, SH_NOATTRIB, false, edict_t *, DIALOG_TYPE, KeyValues *, IServerPluginCallbacks *); ds@1952: ds@1952: ValveMenuStyle g_ValveMenuStyle; ds@1952: extern const char *g_OptionNumTable[]; ds@1952: extern const char *g_OptionCmdTable[]; ds@1952: CallClass *g_pSPHCC = NULL; ds@1952: ds@1952: ValveMenuStyle::ValveMenuStyle() : m_players(new CValveMenuPlayer[256+1]) ds@1952: { ds@1952: } ds@1952: ds@1952: CBaseMenuPlayer *ValveMenuStyle::GetMenuPlayer(int client) ds@1952: { ds@1952: return &m_players[client]; ds@1952: } ds@1952: ds@1952: bool ValveMenuStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd) ds@1952: { ds@1952: if (strcmp(cmdname, "sm_vmenuselect") == 0) ds@1952: { ds@1952: int key_press = atoi(cmd.Arg(1)); ds@1952: g_ValveMenuStyle.ClientPressedKey(client, key_press); ds@1952: return true; ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: void ValveMenuStyle::OnSourceModAllInitialized() ds@1952: { ds@1952: g_Players.AddClientListener(this); ds@1952: SH_ADD_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false); ds@1952: g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers); ds@1952: } ds@1952: ds@1952: void ValveMenuStyle::OnSourceModShutdown() ds@1952: { ds@1952: SH_RELEASE_CALLCLASS(g_pSPHCC); ds@1952: SH_REMOVE_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false); ds@1952: g_Players.RemoveClientListener(this); ds@1952: } ds@1952: ds@1952: void ValveMenuStyle::HookCreateMessage(edict_t *pEdict, ds@1952: DIALOG_TYPE type, ds@1952: KeyValues *kv, ds@1952: IServerPluginCallbacks *plugin) ds@1952: { ds@1952: if (type != DIALOG_MENU) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: int client = engine->IndexOfEdict(pEdict); ds@1952: if (client < 1 || client > 256) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: CValveMenuPlayer *player = &m_players[client]; ds@1952: ds@1952: /* We don't care if the player is in a menu because, for all intents and purposes, ds@1952: * the menu is completely private. Instead, we just figure out the level we'll need ds@1952: * in order to override it. ds@1952: */ ds@1952: player->curPrioLevel = kv->GetInt("level", player->curPrioLevel); ds@1952: ds@1952: /* Oh no! What happens if we got a menu that overwrites ours?! */ ds@1952: if (player->bInMenu) ds@1952: { ds@1952: /* Okay, let the external menu survive for now. It may live another ds@1952: * day to avenge its grandfather, killed in the great Menu Interruption ds@1952: * battle. ds@1952: */ ds@1952: _CancelClientMenu(client, MenuCancel_Interrupted, true); ds@1952: } ds@1952: } ds@1952: ds@1952: IMenuPanel *ValveMenuStyle::CreatePanel() ds@1952: { ds@1952: return new CValveMenuDisplay(); ds@1952: } ds@1952: ds@1952: IBaseMenu *ValveMenuStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) ds@1952: { ds@1952: return new CValveMenu(pHandler, pOwner); ds@1952: } ds@1952: ds@1952: const char *ValveMenuStyle::GetStyleName() ds@1952: { ds@1952: return "valve"; ds@1952: } ds@1952: ds@1952: unsigned int ValveMenuStyle::GetMaxPageItems() ds@1952: { ds@1952: return 8; ds@1952: } ds@1952: ds@1952: void ValveMenuStyle::SendDisplay(int client, IMenuPanel *display) ds@1952: { ds@1952: m_players[client].curPrioLevel--; ds@1952: CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display; ds@1952: vDisplay->SendRawDisplay(client, m_players[client].curPrioLevel, m_players[client].menuHoldTime); ds@1952: } ds@1952: ds@1952: bool ValveMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time) ds@1952: { ds@1952: if (vsp_interface == NULL) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return BaseMenuStyle::DoClientMenu(client, menu, mh, time); ds@1952: } ds@1952: ds@1952: bool ValveMenuStyle::DoClientMenu(int client, CBaseMenu *menu, unsigned int first_item, IMenuHandler *mh, unsigned int time) ds@1952: { ds@1952: if (vsp_interface == NULL) ds@1952: { ds@1952: mh->OnMenuStart(menu); ds@1952: mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); ds@1952: mh->OnMenuEnd(menu, MenuEnd_Cancelled); ds@1952: return false; ds@1952: } ds@1952: ds@1952: return BaseMenuStyle::DoClientMenu(client, menu, first_item, mh, time); ds@1952: } ds@1952: ds@1952: unsigned int ValveMenuStyle::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(ValveMenuStyle) + (sizeof(CValveMenuPlayer) * 257); ds@1952: } ds@1952: ds@1952: CValveMenuDisplay::CValveMenuDisplay() ds@1952: { ds@1952: m_pKv = NULL; ds@1952: Reset(); ds@1952: } ds@1952: ds@1952: CValveMenuDisplay::CValveMenuDisplay(CValveMenu *pMenu) ds@1952: { ds@1952: m_pKv = NULL; ds@1952: Reset(); ds@1952: m_pKv->SetColor("color", pMenu->m_IntroColor); ds@1952: m_pKv->SetString("title", pMenu->m_IntroMsg); ds@1952: } ds@1952: ds@1952: void CValveMenuDisplay::DeleteThis() ds@1952: { ds@1952: delete this; ds@1952: } ds@1952: ds@1952: CValveMenuDisplay::~CValveMenuDisplay() ds@1952: { ds@1952: m_pKv->deleteThis(); ds@1952: } ds@1952: ds@1952: IMenuStyle *CValveMenuDisplay::GetParentStyle() ds@1952: { ds@1952: return &g_ValveMenuStyle; ds@1952: } ds@1952: ds@1952: void CValveMenuDisplay::Reset() ds@1952: { ds@1952: if (m_pKv) ds@1952: { ds@1952: m_pKv->deleteThis(); ds@1952: } ds@1952: m_pKv = new KeyValues("menu"); ds@1952: m_NextPos = 1; ds@1952: m_TitleDrawn = false; ds@1952: } ds@1952: ds@1952: unsigned int CValveMenuDisplay::GetCurrentKey() ds@1952: { ds@1952: return m_NextPos; ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::SetCurrentKey(unsigned int key) ds@1952: { ds@1952: if (key < m_NextPos || key > 9) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: m_NextPos = key; ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::SetExtOption(MenuOption option, const void *valuePtr) ds@1952: { ds@1952: if (option == MenuOption_IntroMessage) ds@1952: { ds@1952: m_pKv->SetString("title", (const char *)valuePtr); ds@1952: return true; ds@1952: } else if (option == MenuOption_IntroColor) { ds@1952: int *array = (int *)valuePtr; ds@1952: m_pKv->SetColor("color", Color(array[0], array[1], array[2], array[3])); ds@1952: return true; ds@1952: } else if (option == MenuOption_Priority) { ds@1952: m_pKv->SetInt("level", *(int *)valuePtr); ds@1952: return true; ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::CanDrawItem(unsigned int drawFlags) ds@1952: { ds@1952: /** ds@1952: * ITEMDRAW_RAWLINE - We can't draw random text, and this doesn't add a slot, ds@1952: * so it's completely safe to ignore it. ds@1952: * ----------------------------------------- ds@1952: */ ds@1952: if (drawFlags & ITEMDRAW_RAWLINE) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: /** ds@1952: * Special cases, explained in DrawItem() ds@1952: */ ds@1952: if ((drawFlags & ITEMDRAW_NOTEXT) ds@1952: || (drawFlags & ITEMDRAW_SPACER)) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: /** ds@1952: * We can't draw disabled text. We could bump the position, but we ds@1952: * want DirectDraw() to find some actual items to display. ds@1952: */ ds@1952: if (drawFlags & ITEMDRAW_DISABLED) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: unsigned int CValveMenuDisplay::DrawItem(const ItemDrawInfo &item) ds@1952: { ds@1952: if (m_NextPos > 9 || !CanDrawItem(item.style)) ds@1952: { ds@1952: return 0; ds@1952: } ds@1952: ds@1952: /** ds@1952: * For these cases we can't draw anything at all, but ds@1952: * we can at least bump the position since we were explicitly asked to. ds@1952: */ ds@1952: if ((item.style & ITEMDRAW_NOTEXT) ds@1952: || (item.style & ITEMDRAW_SPACER)) ds@1952: { ds@1952: return m_NextPos++; ds@1952: } ds@1952: ds@1952: char buffer[255]; ds@1952: UTIL_Format(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display); ds@1952: ds@1952: KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true); ds@1952: ki->SetString("command", g_OptionCmdTable[m_NextPos]); ds@1952: ki->SetString("msg", buffer); ds@1952: ds@1952: return m_NextPos++; ds@1952: } ds@1952: ds@1952: void CValveMenuDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */) ds@1952: { ds@1952: if (onlyIfEmpty && m_TitleDrawn) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: m_pKv->SetString("msg", text); ds@1952: m_TitleDrawn = true; ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::DrawRawLine(const char *rawline) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: void CValveMenuDisplay::SendRawDisplay(int client, int priority, unsigned int time) ds@1952: { ds@1952: m_pKv->SetInt("level", priority); ds@1952: m_pKv->SetInt("time", time ? time : 200); ds@1952: ds@1952: SH_CALL(g_pSPHCC, &IServerPluginHelpers::CreateMessage)( ds@1952: engine->PEntityOfEntIndex(client), ds@1952: DIALOG_MENU, ds@1952: m_pKv, ds@1952: vsp_interface); ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time) ds@1952: { ds@1952: return g_ValveMenuStyle.DoClientMenu(client, this, handler, time); ds@1952: } ds@1952: ds@1952: bool CValveMenuDisplay::SetSelectableKeys(unsigned int keymap) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: int CValveMenuDisplay::GetAmountRemaining() ds@1952: { ds@1952: /* :TODO: this is a lie, but nothing really seems meaningful... */ ds@1952: return -1; ds@1952: } ds@1952: ds@1952: unsigned int CValveMenuDisplay::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(CValveMenuDisplay) + (sizeof(KeyValues) * m_NextPos * 10); ds@1952: } ds@1952: ds@1952: CValveMenu::CValveMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : ds@1952: CBaseMenu(pHandler, &g_ValveMenuStyle, pOwner), ds@1952: m_IntroColor(255, 0, 0, 255) ds@1952: { ds@1952: strcpy(m_IntroMsg, "You have a menu, press ESC"); ds@1952: m_Pagination = 5; ds@1952: } ds@1952: ds@1952: void CValveMenu::Cancel_Finally() ds@1952: { ds@1952: g_ValveMenuStyle.CancelMenu(this); ds@1952: } ds@1952: ds@1952: bool CValveMenu::SetPagination(unsigned int itemsPerPage) ds@1952: { ds@1952: if (itemsPerPage > 5) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return CBaseMenu::SetPagination(itemsPerPage); ds@1952: } ds@1952: ds@1952: bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr) ds@1952: { ds@1952: if (option == MenuOption_IntroMessage) ds@1952: { ds@1952: strncopy(m_IntroMsg, (const char *)valuePtr, sizeof(m_IntroMsg)); ds@1952: return true; ds@1952: } else if (option == MenuOption_IntroColor) { ds@1952: unsigned int *array = (unsigned int *)valuePtr; ds@1952: m_IntroColor = Color(array[0], array[1], array[2], array[3]); ds@1952: return true; ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: bool CValveMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler) ds@1952: { ds@1952: return DisplayAtItem(client, time, 0, alt_handler); ds@1952: } ds@1952: ds@1952: bool CValveMenu::DisplayAtItem(int client, ds@1952: unsigned int time, ds@1952: unsigned int start_item, ds@1952: IMenuHandler *alt_handler/* =NULL */) ds@1952: { ds@1952: if (m_bCancelling) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return g_ValveMenuStyle.DoClientMenu(client, this, start_item, alt_handler ? alt_handler : m_pHandler, time); ds@1952: } ds@1952: ds@1952: IMenuPanel *CValveMenu::CreatePanel() ds@1952: { ds@1952: return new CValveMenuDisplay(this); ds@1952: } ds@1952: ds@1952: void CValveMenu::SetMenuOptionFlags(unsigned int flags) ds@1952: { ds@1952: flags |= MENUFLAG_BUTTON_EXIT; ds@1952: CBaseMenu::SetMenuOptionFlags(flags); ds@1952: } ds@1952: ds@1952: unsigned int CValveMenu::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(CValveMenu) + GetBaseMemUsage(); ds@1952: } ds@1952: ds@1952: const char *g_OptionNumTable[] = ds@1952: { ds@1952: "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" ds@1952: }; ds@1952: ds@1952: const char *g_OptionCmdTable[] = ds@1952: { ds@1952: "sm_vmenuselect 0", /* INVALID! */ ds@1952: "sm_vmenuselect 1", ds@1952: "sm_vmenuselect 2", ds@1952: "sm_vmenuselect 3", ds@1952: "sm_vmenuselect 4", ds@1952: "sm_vmenuselect 5", ds@1952: "sm_vmenuselect 6", ds@1952: "sm_vmenuselect 7", ds@1952: "sm_vmenuselect 8", ds@1952: "sm_vmenuselect 9", ds@1952: "sm_vmenuselect 10" ds@1952: };