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 "MenuStyle_Radio.h" ds@1952: #include "sm_stringutil.h" ds@1952: #include "UserMessages.h" ds@1952: #include "GameConfigs.h" ds@1952: #include "PlayerManager.h" ds@1952: #if defined MENU_DEBUG ds@1952: #include "Logger.h" ds@1952: #endif ds@1952: ds@1952: extern const char *g_RadioNumTable[]; ds@1952: CRadioStyle g_RadioMenuStyle; ds@1952: int g_ShowMenuId = -1; ds@1952: bool g_bRadioInit = false; ds@1952: unsigned int g_RadioMenuTimeout = 0; ds@1952: ds@1952: CRadioStyle::CRadioStyle() ds@1952: { ds@1952: m_players = new CRadioMenuPlayer[256+1]; ds@1952: for (size_t i = 0; i < 256+1; i++) ds@1952: { ds@1952: m_players[i].Radio_SetIndex(i); ds@1952: } ds@1952: } ds@1952: ds@1952: void CRadioStyle::OnSourceModAllInitialized() ds@1952: { ds@1952: g_Players.AddClientListener(this); ds@1952: } ds@1952: ds@1952: void CRadioStyle::OnSourceModLevelChange(const char *mapName) ds@1952: { ds@1952: if (g_bRadioInit) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: g_bRadioInit = true; ds@1952: const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg"); ds@1952: if (!msg || msg[0] == '\0') ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: g_ShowMenuId = g_UserMsgs.GetMessageIndex(msg); ds@1952: ds@1952: if (!IsSupported()) ds@1952: { ds@1952: return; ds@1952: } ds@1952: ds@1952: const char *val = g_pGameConf->GetKeyValue("RadioMenuTimeout"); ds@1952: if (val != NULL) ds@1952: { ds@1952: g_RadioMenuTimeout = atoi(val); ds@1952: } ds@1952: else ds@1952: { ds@1952: g_RadioMenuTimeout = 0; ds@1952: } ds@1952: ds@1952: g_Menus.AddStyle(this); ds@1952: g_Menus.SetDefaultStyle(this); ds@1952: ds@1952: g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false); ds@1952: } ds@1952: ds@1952: void CRadioStyle::OnSourceModShutdown() ds@1952: { ds@1952: g_Players.RemoveClientListener(this); ds@1952: g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false); ds@1952: ds@1952: while (!m_FreeDisplays.empty()) ds@1952: { ds@1952: delete m_FreeDisplays.front(); ds@1952: m_FreeDisplays.pop(); ds@1952: } ds@1952: } ds@1952: ds@1952: bool CRadioStyle::IsSupported() ds@1952: { ds@1952: return (g_ShowMenuId != -1); ds@1952: } ds@1952: ds@1952: bool CRadioStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd) ds@1952: { ds@1952: if (strcmp(cmdname, "menuselect") == 0) ds@1952: { ds@1952: if (!m_players[client].bInMenu) ds@1952: { ds@1952: m_players[client].bInExternMenu = false; ds@1952: return false; ds@1952: } ds@1952: ds@1952: int arg = atoi(cmd.Arg(1)); ds@1952: ClientPressedKey(client, arg); ds@1952: return true; ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: static unsigned int g_last_holdtime = 0; ds@1952: static unsigned int g_last_client_count = 0; ds@1952: static int g_last_clients[256]; ds@1952: ds@1952: void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) ds@1952: { ds@1952: int count = pFilter->GetRecipientCount(); ds@1952: bf_read br(bf->GetBasePointer(), 3); ds@1952: ds@1952: br.ReadWord(); ds@1952: int c = br.ReadChar(); ds@1952: ds@1952: g_last_holdtime = (c == -1) ? 0 : (unsigned)c; ds@1952: ds@1952: for (int i=0; iGetRecipientIndex(i); ds@1952: } ds@1952: } ds@1952: ds@1952: void CRadioStyle::OnUserMessageSent(int msg_id) ds@1952: { ds@1952: for (unsigned int i=0; iSendRawDisplay(client, m_players[client].menuHoldTime); ds@1952: } ds@1952: ds@1952: IMenuPanel *CRadioStyle::CreatePanel() ds@1952: { ds@1952: return g_RadioMenuStyle.MakeRadioDisplay(); ds@1952: } ds@1952: ds@1952: IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) ds@1952: { ds@1952: return new CRadioMenu(pHandler, pOwner); ds@1952: } ds@1952: ds@1952: unsigned int CRadioStyle::GetMaxPageItems() ds@1952: { ds@1952: return 10; ds@1952: } ds@1952: ds@1952: const char *CRadioStyle::GetStyleName() ds@1952: { ds@1952: return "radio"; ds@1952: } ds@1952: ds@1952: CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client) ds@1952: { ds@1952: return &m_players[client]; ds@1952: } ds@1952: ds@1952: CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu) ds@1952: { ds@1952: CRadioDisplay *display; ds@1952: if (m_FreeDisplays.empty()) ds@1952: { ds@1952: display = new CRadioDisplay(); ds@1952: } ds@1952: else ds@1952: { ds@1952: display = m_FreeDisplays.front(); ds@1952: m_FreeDisplays.pop(); ds@1952: display->Reset(); ds@1952: } ds@1952: return display; ds@1952: } ds@1952: ds@1952: IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys) ds@1952: { ds@1952: CRadioDisplay *pPanel = MakeRadioDisplay(NULL); ds@1952: ds@1952: pPanel->DirectSet(str, keys); ds@1952: ds@1952: return pPanel; ds@1952: } ds@1952: ds@1952: void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display) ds@1952: { ds@1952: m_FreeDisplays.push(display); ds@1952: } ds@1952: ds@1952: CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client) ds@1952: { ds@1952: return &m_players[client]; ds@1952: } ds@1952: ds@1952: void CRadioStyle::ProcessWatchList() ds@1952: { ds@1952: if (!g_RadioMenuTimeout) ds@1952: { ds@1952: BaseMenuStyle::ProcessWatchList(); ds@1952: return; ds@1952: } ds@1952: ds@1952: BaseMenuStyle::ProcessWatchList(); ds@1952: ds@1952: CRadioMenuPlayer *pPlayer; ds@1952: unsigned int max_clients = g_Players.GetMaxClients(); ds@1952: for (unsigned int i = 1; i <= max_clients; i++) ds@1952: { ds@1952: pPlayer = GetRadioMenuPlayer(i); ds@1952: if (!pPlayer->bInMenu || pPlayer->bInExternMenu) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: if (pPlayer->Radio_NeedsRefresh()) ds@1952: { ds@1952: pPlayer->Radio_Refresh(); ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: unsigned int CRadioStyle::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(CRadioStyle) + (sizeof(CRadioMenuPlayer) * 257); ds@1952: } ds@1952: ds@1952: CRadioDisplay::CRadioDisplay() ds@1952: { ds@1952: Reset(); ds@1952: } ds@1952: ds@1952: CRadioDisplay::CRadioDisplay(CRadioMenu *menu) ds@1952: { ds@1952: Reset(); ds@1952: } ds@1952: ds@1952: bool CRadioDisplay::DrawRawLine(const char *rawline) ds@1952: { ds@1952: m_BufferText.append(rawline); ds@1952: m_BufferText.append("\n"); ds@1952: return true; ds@1952: } ds@1952: ds@1952: void CRadioDisplay::Reset() ds@1952: { ds@1952: m_BufferText.assign(""); ds@1952: m_Title.assign(""); ds@1952: m_NextPos = 1; ds@1952: keys = 0; ds@1952: } ds@1952: ds@1952: void CRadioDisplay::DirectSet(const char *str, int keymap) ds@1952: { ds@1952: m_Title.clear(); ds@1952: m_BufferText.assign(str); ds@1952: keys = keymap; ds@1952: } ds@1952: ds@1952: unsigned int CRadioDisplay::GetCurrentKey() ds@1952: { ds@1952: return m_NextPos; ds@1952: } ds@1952: ds@1952: bool CRadioDisplay::SetCurrentKey(unsigned int key) ds@1952: { ds@1952: if (key < m_NextPos || m_NextPos > 10) 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 CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time) ds@1952: { ds@1952: return g_RadioMenuStyle.DoClientMenu(client, this, handler, time); ds@1952: } ds@1952: ds@1952: bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: IMenuStyle *CRadioDisplay::GetParentStyle() ds@1952: { ds@1952: return &g_RadioMenuStyle; ds@1952: } ds@1952: ds@1952: void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */) ds@1952: { ds@1952: if (m_Title.size() != 0 && onlyIfEmpty) ds@1952: { ds@1952: return; ds@1952: } ds@1952: m_Title.assign(text); ds@1952: } ds@1952: ds@1952: unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item) ds@1952: { ds@1952: if (m_NextPos > 10 || !CanDrawItem(item.style)) ds@1952: { ds@1952: return 0; ds@1952: } ds@1952: ds@1952: if (item.style & ITEMDRAW_RAWLINE) ds@1952: { ds@1952: if (item.style & ITEMDRAW_SPACER) ds@1952: { ds@1952: m_BufferText.append(" \n"); ds@1952: } else { ds@1952: m_BufferText.append(item.display); ds@1952: m_BufferText.append("\n"); ds@1952: } ds@1952: return 0; ds@1952: } else if (item.style & ITEMDRAW_SPACER) { ds@1952: m_BufferText.append(" \n"); ds@1952: return m_NextPos++; ds@1952: } else if (item.style & ITEMDRAW_NOTEXT) { ds@1952: return m_NextPos++; ds@1952: } ds@1952: ds@1952: if (item.style & ITEMDRAW_DISABLED) ds@1952: { ds@1952: m_BufferText.append(g_RadioNumTable[m_NextPos]); ds@1952: m_BufferText.append(item.display); ds@1952: m_BufferText.append("\n"); ds@1952: } else { ds@1952: m_BufferText.append("->"); ds@1952: m_BufferText.append(g_RadioNumTable[m_NextPos]); ds@1952: m_BufferText.append(item.display); ds@1952: m_BufferText.append("\n"); ds@1952: keys |= (1<<(m_NextPos-1)); ds@1952: } ds@1952: ds@1952: return m_NextPos++; ds@1952: } ds@1952: ds@1952: bool CRadioDisplay::CanDrawItem(unsigned int drawFlags) ds@1952: { ds@1952: if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL)) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: void CRadioDisplay::SendRawDisplay(int client, unsigned int time) ds@1952: { ds@1952: int _sel_keys = (keys == 0) ? (1<<9) : keys; ds@1952: CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client); ds@1952: pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str()); ds@1952: pPlayer->Radio_Refresh(); ds@1952: } ds@1952: ds@1952: void CRadioMenuPlayer::Radio_SetIndex(unsigned int index) ds@1952: { ds@1952: m_index = index; ds@1952: } ds@1952: ds@1952: bool CRadioMenuPlayer::Radio_NeedsRefresh() ds@1952: { ds@1952: return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout); ds@1952: } ds@1952: ds@1952: void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text) ds@1952: { ds@1952: if (title[0] != '\0') ds@1952: { ds@1952: display_len = UTIL_Format(display_pkt, ds@1952: sizeof(display_pkt), ds@1952: "%s\n%s", ds@1952: title, ds@1952: text); ds@1952: } ds@1952: else ds@1952: { ds@1952: display_len = UTIL_Format(display_pkt, ds@1952: sizeof(display_pkt), ds@1952: "%s", ds@1952: text); ds@1952: } ds@1952: display_keys = keys; ds@1952: } ds@1952: ds@1952: void CRadioMenuPlayer::Radio_Refresh() ds@1952: { ds@1952: cell_t players[1] = {m_index}; ds@1952: char *ptr = display_pkt; ds@1952: char save = 0; ds@1952: size_t len = display_len; ds@1952: unsigned int time; ds@1952: ds@1952: /* Compute the new time */ ds@1952: if (menuHoldTime == 0) ds@1952: { ds@1952: time = 0; ds@1952: } ds@1952: else ds@1952: { ds@1952: time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime); ds@1952: } ds@1952: ds@1952: while (true) ds@1952: { ds@1952: if (len > 240) ds@1952: { ds@1952: save = ptr[240]; ds@1952: ptr[240] = '\0'; ds@1952: } ds@1952: bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS); ds@1952: buffer->WriteWord(display_keys); ds@1952: buffer->WriteChar(time ? time : -1); ds@1952: buffer->WriteByte( (len > 240) ? 1 : 0 ); ds@1952: buffer->WriteString(ptr); ds@1952: g_UserMsgs.EndMessage(); ds@1952: if (len > 240) ds@1952: { ds@1952: ptr[240] = save; ds@1952: ptr = &ptr[240]; ds@1952: len -= 240; ds@1952: } ds@1952: else ds@1952: { ds@1952: break; ds@1952: } ds@1952: } ds@1952: ds@1952: display_last_refresh = gpGlobals->curtime; ds@1952: } ds@1952: ds@1952: int CRadioDisplay::GetAmountRemaining() ds@1952: { ds@1952: size_t amt = m_Title.size() + 1 + m_BufferText.size(); ds@1952: if (amt >= 511) ds@1952: { ds@1952: return 0; ds@1952: } ds@1952: return (int)(511 - amt); ds@1952: } ds@1952: ds@1952: void CRadioDisplay::DeleteThis() ds@1952: { ds@1952: delete this; ds@1952: } ds@1952: ds@1952: bool CRadioDisplay::SetSelectableKeys(unsigned int keymap) ds@1952: { ds@1952: keys = (signed)keymap; ds@1952: return true; ds@1952: } ds@1952: ds@1952: unsigned int CRadioDisplay::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(CRadioDisplay) ds@1952: + m_BufferText.size() ds@1952: + m_Title.size(); ds@1952: } ds@1952: ds@1952: CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : ds@1952: CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner) ds@1952: { ds@1952: } ds@1952: ds@1952: bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: IMenuPanel *CRadioMenu::CreatePanel() ds@1952: { ds@1952: return g_RadioMenuStyle.MakeRadioDisplay(this); ds@1952: } ds@1952: ds@1952: bool CRadioMenu::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 CRadioMenu::DisplayAtItem(int client, ds@1952: unsigned int time, ds@1952: unsigned int start_item, ds@1952: IMenuHandler *alt_handler) ds@1952: { ds@1952: #if defined MENU_DEBUG ds@1952: g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)", ds@1952: this, ds@1952: client, ds@1952: time); ds@1952: #endif ds@1952: if (m_bCancelling) ds@1952: { ds@1952: return false; ds@1952: } ds@1952: ds@1952: return g_RadioMenuStyle.DoClientMenu(client, ds@1952: this, ds@1952: start_item, ds@1952: alt_handler ? alt_handler : m_pHandler, ds@1952: time); ds@1952: } ds@1952: ds@1952: void CRadioMenu::Cancel_Finally() ds@1952: { ds@1952: g_RadioMenuStyle.CancelMenu(this); ds@1952: } ds@1952: ds@1952: unsigned int CRadioMenu::GetApproxMemUsage() ds@1952: { ds@1952: return sizeof(CRadioMenu) + GetBaseMemUsage(); ds@1952: } ds@1952: ds@1952: const char *g_RadioNumTable[11] = ds@1952: { ds@1952: "0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. " ds@1952: };