core/MenuStyle_Radio.cpp
author Asher Baker <asherkin@gmail.com>
Sat Sep 08 21:02:46 2012 +0100 (2012-09-08)
changeset 3652 e0a6517dcd89
parent 3570 cfdf2ed4c724
child 3743 a98686a77974
permissions -rw-r--r--
Include the auto-version file in the build, so 3rd party plugins compiled aginst the includes get useful versioning info (bug 5453, r=dvander).
     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 // back, next, exit
    49 #define MAX_PAGINATION_OPTIONS 3
    50 
    51 #define MAX_MENUSLOT_KEYS 10
    52 
    53 static unsigned int s_RadioMaxPageItems = MAX_MENUSLOT_KEYS;
    54 
    55 
    56 CRadioStyle::CRadioStyle()
    57 {
    58 	m_players = new CRadioMenuPlayer[256+1];
    59 	for (size_t i = 0; i < 256+1; i++)
    60 	{
    61 		m_players[i].Radio_SetIndex(i);
    62 	}
    63 }
    64 
    65 void CRadioStyle::OnSourceModAllInitialized()
    66 {
    67 	g_Players.AddClientListener(this);
    68 }
    69 
    70 void CRadioStyle::OnSourceModLevelChange(const char *mapName)
    71 {
    72 	if (g_bRadioInit)
    73 	{
    74 		return;
    75 	}
    76 
    77 	g_bRadioInit = true;
    78 	const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg");
    79 	if (!msg || msg[0] == '\0')
    80 	{
    81 		return;
    82 	}
    83 
    84 	g_ShowMenuId = g_UserMsgs.GetMessageIndex(msg);
    85 
    86 	if (!IsSupported())
    87 	{
    88 		return;
    89 	}
    90 
    91 	const char *val = g_pGameConf->GetKeyValue("RadioMenuTimeout");
    92 	if (val != NULL)
    93 	{
    94 		g_RadioMenuTimeout = atoi(val);
    95 	}
    96 	else
    97 	{
    98 		g_RadioMenuTimeout = 0;
    99 	}
   100 
   101 	const char *items = g_pGameConf->GetKeyValue("RadioMenuMaxPageItems");
   102 	if (items != NULL)
   103 	{
   104 		int value = atoi(items);
   105 
   106 		// Only override the mostly-safe default if it's a sane value
   107 		if (value > MAX_PAGINATION_OPTIONS && value <= MAX_MENUSLOT_KEYS)
   108 		{
   109 			s_RadioMaxPageItems = value;
   110 		}
   111 	}
   112 
   113 	g_Menus.AddStyle(this);
   114 	g_Menus.SetDefaultStyle(this);
   115 
   116 	g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
   117 }
   118 
   119 void CRadioStyle::OnSourceModShutdown()
   120 {
   121 	g_Players.RemoveClientListener(this);
   122 	g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false);
   123 
   124 	while (!m_FreeDisplays.empty())
   125 	{
   126 		delete m_FreeDisplays.front();
   127 		m_FreeDisplays.pop();
   128 	}
   129 }
   130 
   131 bool CRadioStyle::IsSupported()
   132 {
   133 	return (g_ShowMenuId != -1);
   134 }
   135 
   136 bool CRadioStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd)
   137 {
   138 	if (strcmp(cmdname, "menuselect") == 0)
   139 	{
   140 		if (!m_players[client].bInMenu)
   141 		{
   142 			m_players[client].bInExternMenu = false;
   143 			return false;
   144 		}
   145 
   146 		int arg = atoi(cmd.Arg(1));
   147 		ClientPressedKey(client, arg);
   148 		return true;
   149 	}
   150 
   151 	return false;
   152 }
   153 
   154 static unsigned int g_last_holdtime = 0;
   155 static unsigned int g_last_client_count = 0;
   156 static int g_last_clients[256];
   157 
   158 void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   159 {
   160 	int count = pFilter->GetRecipientCount();
   161 	bf_read br(bf->GetBasePointer(), 3);
   162 
   163 	br.ReadWord();
   164 	int c = br.ReadChar();
   165 
   166 	g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
   167 
   168 	for (int i=0; i<count; i++)
   169 	{
   170 		g_last_clients[g_last_client_count++] = pFilter->GetRecipientIndex(i);
   171 	}
   172 }
   173 
   174 void CRadioStyle::OnUserMessageSent(int msg_id)
   175 {
   176 	for (unsigned int i=0; i<g_last_client_count; i++)
   177 	{
   178 		int client = g_last_clients[i];
   179 #if defined MENU_DEBUG
   180 		g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
   181 			client,
   182 			m_players[client].bInExternMenu);
   183 #endif
   184 		if (m_players[client].bInMenu)
   185 		{
   186 			_CancelClientMenu(client, MenuCancel_Interrupted, true);
   187 		}
   188 		m_players[client].bInExternMenu = true;
   189 		m_players[client].menuHoldTime = g_last_holdtime;
   190 	}
   191 	g_last_client_count = 0;
   192 }
   193 
   194 void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
   195 {
   196 	CRadioDisplay *rDisplay = (CRadioDisplay *)display;
   197 	rDisplay->SendRawDisplay(client, m_players[client].menuHoldTime);
   198 }
   199 
   200 IMenuPanel *CRadioStyle::CreatePanel()
   201 {
   202 	return g_RadioMenuStyle.MakeRadioDisplay();
   203 }
   204 
   205 IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
   206 {
   207 	return new CRadioMenu(pHandler, pOwner);
   208 }
   209 
   210 unsigned int CRadioStyle::GetMaxPageItems()
   211 {
   212 	return s_RadioMaxPageItems;
   213 }
   214 
   215 const char *CRadioStyle::GetStyleName()
   216 {
   217 	return "radio";
   218 }
   219 
   220 CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
   221 {
   222 	return &m_players[client];
   223 }
   224 
   225 CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
   226 {
   227 	CRadioDisplay *display;
   228 	if (m_FreeDisplays.empty())
   229 	{
   230 		display = new CRadioDisplay();
   231 	}
   232 	else
   233 	{
   234 		display = m_FreeDisplays.front();
   235 		m_FreeDisplays.pop();
   236 		display->Reset();
   237 	}
   238 	return display;
   239 }
   240 
   241 IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys)
   242 {
   243 	CRadioDisplay *pPanel = MakeRadioDisplay(NULL);
   244 
   245 	pPanel->DirectSet(str, keys);
   246 
   247 	return pPanel;
   248 }
   249 
   250 void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
   251 {
   252 	m_FreeDisplays.push(display);
   253 }
   254 
   255 CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client)
   256 {
   257 	return &m_players[client];
   258 }
   259 
   260 void CRadioStyle::ProcessWatchList()
   261 {
   262 	if (!g_RadioMenuTimeout)
   263 	{
   264 		BaseMenuStyle::ProcessWatchList();
   265 		return;
   266 	}
   267 
   268 	BaseMenuStyle::ProcessWatchList();
   269 
   270 	CRadioMenuPlayer *pPlayer;
   271 	unsigned int max_clients = g_Players.GetMaxClients();
   272 	for (unsigned int i = 1; i <= max_clients; i++)
   273 	{
   274 		pPlayer = GetRadioMenuPlayer(i);
   275 		if (!pPlayer->bInMenu || pPlayer->bInExternMenu)
   276 		{
   277 			continue;
   278 		}
   279 		if (pPlayer->Radio_NeedsRefresh())
   280 		{
   281 			pPlayer->Radio_Refresh();
   282 		}
   283 	}
   284 }
   285 
   286 unsigned int CRadioStyle::GetApproxMemUsage()
   287 {
   288 	return sizeof(CRadioStyle) + (sizeof(CRadioMenuPlayer) * 257);
   289 }
   290 
   291 CRadioDisplay::CRadioDisplay()
   292 {
   293 	Reset();
   294 }
   295 
   296 CRadioDisplay::CRadioDisplay(CRadioMenu *menu)
   297 {
   298 	Reset();
   299 }
   300 
   301 bool CRadioDisplay::DrawRawLine(const char *rawline)
   302 {
   303 	m_BufferText.append(rawline);
   304 	m_BufferText.append("\n");
   305 	return true;
   306 }
   307 
   308 void CRadioDisplay::Reset()
   309 {
   310 	m_BufferText.assign("");
   311 	m_Title.assign("");
   312 	m_NextPos = 1;
   313 	keys = 0;
   314 }
   315 
   316 void CRadioDisplay::DirectSet(const char *str, int keymap)
   317 {
   318 	m_Title.clear();
   319 	m_BufferText.assign(str);
   320 	keys = keymap;
   321 }
   322 
   323 unsigned int CRadioDisplay::GetCurrentKey()
   324 {
   325 	return m_NextPos;
   326 }
   327 
   328 bool CRadioDisplay::SetCurrentKey(unsigned int key)
   329 {
   330 	if (key < m_NextPos || m_NextPos > s_RadioMaxPageItems)
   331 	{
   332 		return false;
   333 	}
   334 
   335 	m_NextPos = key;
   336 
   337 	return true;
   338 }
   339 
   340 bool CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
   341 {
   342 	return g_RadioMenuStyle.DoClientMenu(client, this, handler, time);
   343 }
   344 
   345 bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr)
   346 {
   347 	return false;
   348 }
   349 
   350 IMenuStyle *CRadioDisplay::GetParentStyle()
   351 {
   352 	return &g_RadioMenuStyle;
   353 }
   354 
   355 void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
   356 {
   357 	if (m_Title.size() != 0 && onlyIfEmpty)
   358 	{
   359 		return;
   360 	}
   361 	m_Title.assign(text);
   362 }
   363 
   364 unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
   365 {
   366 	if (m_NextPos > s_RadioMaxPageItems || !CanDrawItem(item.style))
   367 	{
   368 		return 0;
   369 	}
   370 
   371 	if (item.style & ITEMDRAW_RAWLINE)
   372 	{
   373 		if (item.style & ITEMDRAW_SPACER)
   374 		{
   375 			m_BufferText.append(" \n");
   376 		} else {
   377 			m_BufferText.append(item.display);
   378 			m_BufferText.append("\n");
   379 		}
   380 		return 0;
   381 	} else if (item.style & ITEMDRAW_SPACER) {
   382 		m_BufferText.append(" \n");
   383 		return m_NextPos++;
   384 	} else if (item.style & ITEMDRAW_NOTEXT) {
   385 		return m_NextPos++;
   386 	}
   387 
   388 	if (item.style & ITEMDRAW_DISABLED)
   389 	{
   390 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   391 		m_BufferText.append(item.display);
   392 		m_BufferText.append("\n");
   393 	} else {
   394 		m_BufferText.append("->");
   395 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   396 		m_BufferText.append(item.display);
   397 		m_BufferText.append("\n");
   398 		keys |= (1<<(m_NextPos-1));
   399 	}
   400 
   401 	return m_NextPos++;
   402 }
   403 
   404 bool CRadioDisplay::CanDrawItem(unsigned int drawFlags)
   405 {
   406 	if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
   407 	{
   408 		return false;
   409 	}
   410 
   411 	if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL))
   412 	{
   413 		return false;
   414 	}
   415 
   416 	return true;
   417 }
   418 
   419 void CRadioDisplay::SendRawDisplay(int client, unsigned int time)
   420 {
   421 	int _sel_keys = (keys == 0) ? (1<<9) : keys;
   422 	CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client);
   423 	pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str());
   424 	pPlayer->Radio_Refresh();
   425 }
   426 
   427 void CRadioMenuPlayer::Radio_SetIndex(unsigned int index)
   428 {
   429 	m_index = index;
   430 }
   431 
   432 bool CRadioMenuPlayer::Radio_NeedsRefresh()
   433 {
   434 	return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout);
   435 }
   436 
   437 void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
   438 {
   439 	if (title[0] != '\0')
   440 	{
   441 		display_len = UTIL_Format(display_pkt, 
   442 			sizeof(display_pkt), 
   443 			"%s\n%s", 
   444 			title,
   445 			text);
   446 	}
   447 	else
   448 	{
   449 		display_len = UTIL_Format(display_pkt, 
   450 			sizeof(display_pkt), 
   451 			"%s", 
   452 			text);
   453 	}
   454 	display_keys = keys;
   455 }
   456 
   457 void CRadioMenuPlayer::Radio_Refresh()
   458 {
   459 	cell_t players[1] = {m_index};
   460 	char *ptr = display_pkt;
   461 	char save = 0;
   462 	size_t len = display_len;
   463 	unsigned int time;
   464 
   465 	/* Compute the new time */
   466 	if (menuHoldTime == 0)
   467 	{
   468 		time = 0;
   469 	}
   470 	else
   471 	{
   472 		time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
   473 	}
   474 
   475 	while (true)
   476 	{
   477 		if (len > 240)
   478 		{
   479 			save = ptr[240];
   480 			ptr[240] = '\0';
   481 		}
   482 		bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
   483 		buffer->WriteWord(display_keys);
   484 		buffer->WriteChar(time ? time : -1);
   485 		buffer->WriteByte( (len > 240) ? 1 : 0 );
   486 		buffer->WriteString(ptr);
   487 		g_UserMsgs.EndMessage();
   488 		if (len > 240)
   489 		{
   490 			ptr[240] = save;
   491 			ptr = &ptr[240];
   492 			len -= 240;
   493 		}
   494 		else
   495 		{
   496 			break;
   497 		}
   498 	}
   499 
   500 	display_last_refresh = gpGlobals->curtime;
   501 }
   502 
   503 int CRadioDisplay::GetAmountRemaining()
   504 {
   505 	size_t amt = m_Title.size() + 1 + m_BufferText.size();
   506 	if (amt >= 511)
   507 	{
   508 		return 0;
   509 	}
   510 	return (int)(511 - amt);
   511 }
   512 
   513 void CRadioDisplay::DeleteThis()
   514 {
   515 	delete this;
   516 }
   517 
   518 bool CRadioDisplay::SetSelectableKeys(unsigned int keymap)
   519 {
   520 	keys = (signed)keymap;
   521 	return true;
   522 }
   523 
   524 unsigned int CRadioDisplay::GetApproxMemUsage()
   525 {
   526 	return sizeof(CRadioDisplay)
   527 		+ m_BufferText.size()
   528 		+ m_Title.size();
   529 }
   530 
   531 CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : 
   532 CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
   533 {
   534 	m_Pagination = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
   535 }
   536 
   537 bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
   538 {
   539 	return false;
   540 }
   541 
   542 IMenuPanel *CRadioMenu::CreatePanel()
   543 {
   544 	return g_RadioMenuStyle.MakeRadioDisplay(this);
   545 }
   546 
   547 bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
   548 {
   549 	return DisplayAtItem(client, time, 0, alt_handler);
   550 }
   551 
   552 bool CRadioMenu::DisplayAtItem(int client,
   553 							   unsigned int time,
   554 							   unsigned int start_item,
   555 							   IMenuHandler *alt_handler)
   556 {
   557 #if defined MENU_DEBUG
   558 	g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
   559 		this,
   560 		client,
   561 		time);
   562 #endif
   563 	if (m_bCancelling)
   564 	{
   565 		return false;
   566 	}
   567 
   568 	return g_RadioMenuStyle.DoClientMenu(client,
   569 		this,
   570 		start_item,
   571 		alt_handler ? alt_handler : m_pHandler, 
   572 		time);
   573 }
   574 
   575 bool CRadioMenu::SetPagination(unsigned int itemsPerPage)
   576 {
   577 	const unsigned int maxPerPage = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
   578 	if (itemsPerPage > maxPerPage)
   579 	{
   580 		return false;
   581 	}
   582 
   583 	return CBaseMenu::SetPagination(itemsPerPage);
   584 }
   585 
   586 void CRadioMenu::Cancel_Finally()
   587 {
   588 	g_RadioMenuStyle.CancelMenu(this);
   589 }
   590 
   591 unsigned int CRadioMenu::GetApproxMemUsage()
   592 {
   593 	return sizeof(CRadioMenu) + GetBaseMemUsage();
   594 }
   595 
   596 const char *g_RadioNumTable[11] = 
   597 {
   598 	"0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. "
   599 };