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