core/MenuStyle_Radio.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Mon Jun 27 09:30:52 2011 -0400 (2011-06-27)
changeset 3320 f1ccb4a17a06
parent 3010 8ea042c00c80
child 3570 cfdf2ed4c724
permissions -rw-r--r--
Added support for gamedata lookups from matchmaking_ds lib (bug 4158, r=Fyren).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "MenuStyle_Radio.h"
    33 #include "sm_stringutil.h"
    34 #include "UserMessages.h"
    35 #include <IGameConfigs.h>
    36 #include "PlayerManager.h"
    37 #if defined MENU_DEBUG
    38 #include "Logger.h"
    39 #endif
    40 #include "logic_bridge.h"
    41 
    42 extern const char *g_RadioNumTable[];
    43 CRadioStyle g_RadioMenuStyle;
    44 int g_ShowMenuId = -1;
    45 bool g_bRadioInit = false;
    46 unsigned int g_RadioMenuTimeout = 0;
    47 
    48 CRadioStyle::CRadioStyle()
    49 {
    50 	m_players = new CRadioMenuPlayer[256+1];
    51 	for (size_t i = 0; i < 256+1; i++)
    52 	{
    53 		m_players[i].Radio_SetIndex(i);
    54 	}
    55 }
    56 
    57 void CRadioStyle::OnSourceModAllInitialized()
    58 {
    59 	g_Players.AddClientListener(this);
    60 }
    61 
    62 void CRadioStyle::OnSourceModLevelChange(const char *mapName)
    63 {
    64 	if (g_bRadioInit)
    65 	{
    66 		return;
    67 	}
    68 
    69 	g_bRadioInit = true;
    70 	const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg");
    71 	if (!msg || msg[0] == '\0')
    72 	{
    73 		return;
    74 	}
    75 
    76 	g_ShowMenuId = g_UserMsgs.GetMessageIndex(msg);
    77 
    78 	if (!IsSupported())
    79 	{
    80 		return;
    81 	}
    82 
    83 	const char *val = g_pGameConf->GetKeyValue("RadioMenuTimeout");
    84 	if (val != NULL)
    85 	{
    86 		g_RadioMenuTimeout = atoi(val);
    87 	}
    88 	else
    89 	{
    90 		g_RadioMenuTimeout = 0;
    91 	}
    92 
    93 	g_Menus.AddStyle(this);
    94 	g_Menus.SetDefaultStyle(this);
    95 
    96 	g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
    97 }
    98 
    99 void CRadioStyle::OnSourceModShutdown()
   100 {
   101 	g_Players.RemoveClientListener(this);
   102 	g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false);
   103 
   104 	while (!m_FreeDisplays.empty())
   105 	{
   106 		delete m_FreeDisplays.front();
   107 		m_FreeDisplays.pop();
   108 	}
   109 }
   110 
   111 bool CRadioStyle::IsSupported()
   112 {
   113 	return (g_ShowMenuId != -1);
   114 }
   115 
   116 bool CRadioStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd)
   117 {
   118 	if (strcmp(cmdname, "menuselect") == 0)
   119 	{
   120 		if (!m_players[client].bInMenu)
   121 		{
   122 			m_players[client].bInExternMenu = false;
   123 			return false;
   124 		}
   125 
   126 		int arg = atoi(cmd.Arg(1));
   127 		ClientPressedKey(client, arg);
   128 		return true;
   129 	}
   130 
   131 	return false;
   132 }
   133 
   134 static unsigned int g_last_holdtime = 0;
   135 static unsigned int g_last_client_count = 0;
   136 static int g_last_clients[256];
   137 
   138 void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   139 {
   140 	int count = pFilter->GetRecipientCount();
   141 	bf_read br(bf->GetBasePointer(), 3);
   142 
   143 	br.ReadWord();
   144 	int c = br.ReadChar();
   145 
   146 	g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
   147 
   148 	for (int i=0; i<count; i++)
   149 	{
   150 		g_last_clients[g_last_client_count++] = pFilter->GetRecipientIndex(i);
   151 	}
   152 }
   153 
   154 void CRadioStyle::OnUserMessageSent(int msg_id)
   155 {
   156 	for (unsigned int i=0; i<g_last_client_count; i++)
   157 	{
   158 		int client = g_last_clients[i];
   159 #if defined MENU_DEBUG
   160 		g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
   161 			client,
   162 			m_players[client].bInExternMenu);
   163 #endif
   164 		if (m_players[client].bInMenu)
   165 		{
   166 			_CancelClientMenu(client, MenuCancel_Interrupted, true);
   167 		}
   168 		m_players[client].bInExternMenu = true;
   169 		m_players[client].menuHoldTime = g_last_holdtime;
   170 	}
   171 	g_last_client_count = 0;
   172 }
   173 
   174 void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
   175 {
   176 	CRadioDisplay *rDisplay = (CRadioDisplay *)display;
   177 	rDisplay->SendRawDisplay(client, m_players[client].menuHoldTime);
   178 }
   179 
   180 IMenuPanel *CRadioStyle::CreatePanel()
   181 {
   182 	return g_RadioMenuStyle.MakeRadioDisplay();
   183 }
   184 
   185 IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
   186 {
   187 	return new CRadioMenu(pHandler, pOwner);
   188 }
   189 
   190 unsigned int CRadioStyle::GetMaxPageItems()
   191 {
   192 	return 10;
   193 }
   194 
   195 const char *CRadioStyle::GetStyleName()
   196 {
   197 	return "radio";
   198 }
   199 
   200 CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
   201 {
   202 	return &m_players[client];
   203 }
   204 
   205 CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
   206 {
   207 	CRadioDisplay *display;
   208 	if (m_FreeDisplays.empty())
   209 	{
   210 		display = new CRadioDisplay();
   211 	}
   212 	else
   213 	{
   214 		display = m_FreeDisplays.front();
   215 		m_FreeDisplays.pop();
   216 		display->Reset();
   217 	}
   218 	return display;
   219 }
   220 
   221 IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys)
   222 {
   223 	CRadioDisplay *pPanel = MakeRadioDisplay(NULL);
   224 
   225 	pPanel->DirectSet(str, keys);
   226 
   227 	return pPanel;
   228 }
   229 
   230 void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
   231 {
   232 	m_FreeDisplays.push(display);
   233 }
   234 
   235 CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client)
   236 {
   237 	return &m_players[client];
   238 }
   239 
   240 void CRadioStyle::ProcessWatchList()
   241 {
   242 	if (!g_RadioMenuTimeout)
   243 	{
   244 		BaseMenuStyle::ProcessWatchList();
   245 		return;
   246 	}
   247 
   248 	BaseMenuStyle::ProcessWatchList();
   249 
   250 	CRadioMenuPlayer *pPlayer;
   251 	unsigned int max_clients = g_Players.GetMaxClients();
   252 	for (unsigned int i = 1; i <= max_clients; i++)
   253 	{
   254 		pPlayer = GetRadioMenuPlayer(i);
   255 		if (!pPlayer->bInMenu || pPlayer->bInExternMenu)
   256 		{
   257 			continue;
   258 		}
   259 		if (pPlayer->Radio_NeedsRefresh())
   260 		{
   261 			pPlayer->Radio_Refresh();
   262 		}
   263 	}
   264 }
   265 
   266 unsigned int CRadioStyle::GetApproxMemUsage()
   267 {
   268 	return sizeof(CRadioStyle) + (sizeof(CRadioMenuPlayer) * 257);
   269 }
   270 
   271 CRadioDisplay::CRadioDisplay()
   272 {
   273 	Reset();
   274 }
   275 
   276 CRadioDisplay::CRadioDisplay(CRadioMenu *menu)
   277 {
   278 	Reset();
   279 }
   280 
   281 bool CRadioDisplay::DrawRawLine(const char *rawline)
   282 {
   283 	m_BufferText.append(rawline);
   284 	m_BufferText.append("\n");
   285 	return true;
   286 }
   287 
   288 void CRadioDisplay::Reset()
   289 {
   290 	m_BufferText.assign("");
   291 	m_Title.assign("");
   292 	m_NextPos = 1;
   293 	keys = 0;
   294 }
   295 
   296 void CRadioDisplay::DirectSet(const char *str, int keymap)
   297 {
   298 	m_Title.clear();
   299 	m_BufferText.assign(str);
   300 	keys = keymap;
   301 }
   302 
   303 unsigned int CRadioDisplay::GetCurrentKey()
   304 {
   305 	return m_NextPos;
   306 }
   307 
   308 bool CRadioDisplay::SetCurrentKey(unsigned int key)
   309 {
   310 	if (key < m_NextPos || m_NextPos > 10)
   311 	{
   312 		return false;
   313 	}
   314 
   315 	m_NextPos = key;
   316 
   317 	return true;
   318 }
   319 
   320 bool CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
   321 {
   322 	return g_RadioMenuStyle.DoClientMenu(client, this, handler, time);
   323 }
   324 
   325 bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr)
   326 {
   327 	return false;
   328 }
   329 
   330 IMenuStyle *CRadioDisplay::GetParentStyle()
   331 {
   332 	return &g_RadioMenuStyle;
   333 }
   334 
   335 void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
   336 {
   337 	if (m_Title.size() != 0 && onlyIfEmpty)
   338 	{
   339 		return;
   340 	}
   341 	m_Title.assign(text);
   342 }
   343 
   344 unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
   345 {
   346 	if (m_NextPos > 10 || !CanDrawItem(item.style))
   347 	{
   348 		return 0;
   349 	}
   350 
   351 	if (item.style & ITEMDRAW_RAWLINE)
   352 	{
   353 		if (item.style & ITEMDRAW_SPACER)
   354 		{
   355 			m_BufferText.append(" \n");
   356 		} else {
   357 			m_BufferText.append(item.display);
   358 			m_BufferText.append("\n");
   359 		}
   360 		return 0;
   361 	} else if (item.style & ITEMDRAW_SPACER) {
   362 		m_BufferText.append(" \n");
   363 		return m_NextPos++;
   364 	} else if (item.style & ITEMDRAW_NOTEXT) {
   365 		return m_NextPos++;
   366 	}
   367 
   368 	if (item.style & ITEMDRAW_DISABLED)
   369 	{
   370 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   371 		m_BufferText.append(item.display);
   372 		m_BufferText.append("\n");
   373 	} else {
   374 		m_BufferText.append("->");
   375 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   376 		m_BufferText.append(item.display);
   377 		m_BufferText.append("\n");
   378 		keys |= (1<<(m_NextPos-1));
   379 	}
   380 
   381 	return m_NextPos++;
   382 }
   383 
   384 bool CRadioDisplay::CanDrawItem(unsigned int drawFlags)
   385 {
   386 	if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
   387 	{
   388 		return false;
   389 	}
   390 
   391 	if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL))
   392 	{
   393 		return false;
   394 	}
   395 
   396 	return true;
   397 }
   398 
   399 void CRadioDisplay::SendRawDisplay(int client, unsigned int time)
   400 {
   401 	int _sel_keys = (keys == 0) ? (1<<9) : keys;
   402 	CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client);
   403 	pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str());
   404 	pPlayer->Radio_Refresh();
   405 }
   406 
   407 void CRadioMenuPlayer::Radio_SetIndex(unsigned int index)
   408 {
   409 	m_index = index;
   410 }
   411 
   412 bool CRadioMenuPlayer::Radio_NeedsRefresh()
   413 {
   414 	return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout);
   415 }
   416 
   417 void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
   418 {
   419 	if (title[0] != '\0')
   420 	{
   421 		display_len = UTIL_Format(display_pkt, 
   422 			sizeof(display_pkt), 
   423 			"%s\n%s", 
   424 			title,
   425 			text);
   426 	}
   427 	else
   428 	{
   429 		display_len = UTIL_Format(display_pkt, 
   430 			sizeof(display_pkt), 
   431 			"%s", 
   432 			text);
   433 	}
   434 	display_keys = keys;
   435 }
   436 
   437 void CRadioMenuPlayer::Radio_Refresh()
   438 {
   439 	cell_t players[1] = {m_index};
   440 	char *ptr = display_pkt;
   441 	char save = 0;
   442 	size_t len = display_len;
   443 	unsigned int time;
   444 
   445 	/* Compute the new time */
   446 	if (menuHoldTime == 0)
   447 	{
   448 		time = 0;
   449 	}
   450 	else
   451 	{
   452 		time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
   453 	}
   454 
   455 	while (true)
   456 	{
   457 		if (len > 240)
   458 		{
   459 			save = ptr[240];
   460 			ptr[240] = '\0';
   461 		}
   462 		bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
   463 		buffer->WriteWord(display_keys);
   464 		buffer->WriteChar(time ? time : -1);
   465 		buffer->WriteByte( (len > 240) ? 1 : 0 );
   466 		buffer->WriteString(ptr);
   467 		g_UserMsgs.EndMessage();
   468 		if (len > 240)
   469 		{
   470 			ptr[240] = save;
   471 			ptr = &ptr[240];
   472 			len -= 240;
   473 		}
   474 		else
   475 		{
   476 			break;
   477 		}
   478 	}
   479 
   480 	display_last_refresh = gpGlobals->curtime;
   481 }
   482 
   483 int CRadioDisplay::GetAmountRemaining()
   484 {
   485 	size_t amt = m_Title.size() + 1 + m_BufferText.size();
   486 	if (amt >= 511)
   487 	{
   488 		return 0;
   489 	}
   490 	return (int)(511 - amt);
   491 }
   492 
   493 void CRadioDisplay::DeleteThis()
   494 {
   495 	delete this;
   496 }
   497 
   498 bool CRadioDisplay::SetSelectableKeys(unsigned int keymap)
   499 {
   500 	keys = (signed)keymap;
   501 	return true;
   502 }
   503 
   504 unsigned int CRadioDisplay::GetApproxMemUsage()
   505 {
   506 	return sizeof(CRadioDisplay)
   507 		+ m_BufferText.size()
   508 		+ m_Title.size();
   509 }
   510 
   511 CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : 
   512 CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
   513 {
   514 }
   515 
   516 bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
   517 {
   518 	return false;
   519 }
   520 
   521 IMenuPanel *CRadioMenu::CreatePanel()
   522 {
   523 	return g_RadioMenuStyle.MakeRadioDisplay(this);
   524 }
   525 
   526 bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
   527 {
   528 	return DisplayAtItem(client, time, 0, alt_handler);
   529 }
   530 
   531 bool CRadioMenu::DisplayAtItem(int client,
   532 							   unsigned int time,
   533 							   unsigned int start_item,
   534 							   IMenuHandler *alt_handler)
   535 {
   536 #if defined MENU_DEBUG
   537 	g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
   538 		this,
   539 		client,
   540 		time);
   541 #endif
   542 	if (m_bCancelling)
   543 	{
   544 		return false;
   545 	}
   546 
   547 	return g_RadioMenuStyle.DoClientMenu(client,
   548 		this,
   549 		start_item,
   550 		alt_handler ? alt_handler : m_pHandler, 
   551 		time);
   552 }
   553 
   554 void CRadioMenu::Cancel_Finally()
   555 {
   556 	g_RadioMenuStyle.CancelMenu(this);
   557 }
   558 
   559 unsigned int CRadioMenu::GetApproxMemUsage()
   560 {
   561 	return sizeof(CRadioMenu) + GetBaseMemUsage();
   562 }
   563 
   564 const char *g_RadioNumTable[11] = 
   565 {
   566 	"0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. "
   567 };