core/MenuStyle_Radio.cpp
author David Anderson <dvander@alliedmods.net>
Sun Nov 18 17:07:32 2007 +0000 (2007-11-18)
changeset 1695 1c59f0a5e596
parent 1541 e46ad208b9b4
child 1696 35669010a13f
permissions -rw-r--r--
we now refresh tf2 menus because valve like writing buggy interfaces
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 			return false;
   122 		}
   123 
   124 		int arg = atoi(cmd.Arg(1));
   125 		ClientPressedKey(client, arg);
   126 		return true;
   127 	}
   128 
   129 	return false;
   130 }
   131 
   132 static unsigned int g_last_holdtime = 0;
   133 static unsigned int g_last_client_count = 0;
   134 static int g_last_clients[256];
   135 
   136 void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   137 {
   138 	int count = pFilter->GetRecipientCount();
   139 	bf_read br(bf->GetBasePointer(), 2);
   140 
   141 	br.ReadWord();
   142 	int c = br.ReadChar();
   143 
   144 	g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
   145 
   146 	for (int i=0; i<count; i++)
   147 	{
   148 		g_last_clients[g_last_client_count++] = pFilter->GetRecipientIndex(i);
   149 	}
   150 }
   151 
   152 void CRadioStyle::OnUserMessageSent(int msg_id)
   153 {
   154 	for (unsigned int i=0; i<g_last_client_count; i++)
   155 	{
   156 		int client = g_last_clients[i];
   157 #if defined MENU_DEBUG
   158 		g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
   159 			client,
   160 			m_players[client].bInExternMenu);
   161 #endif
   162 		if (m_players[client].bInMenu)
   163 		{
   164 			_CancelClientMenu(client, MenuCancel_Interrupted, true);
   165 		}
   166 		m_players[client].bInExternMenu = true;
   167 		m_players[client].menuHoldTime = g_last_holdtime;
   168 	}
   169 	g_last_client_count = 0;
   170 }
   171 
   172 void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
   173 {
   174 	CRadioDisplay *rDisplay = (CRadioDisplay *)display;
   175 	rDisplay->SendRawDisplay(client, m_players[client].menuHoldTime);
   176 }
   177 
   178 IMenuPanel *CRadioStyle::CreatePanel()
   179 {
   180 	return g_RadioMenuStyle.MakeRadioDisplay();
   181 }
   182 
   183 IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
   184 {
   185 	return new CRadioMenu(pHandler, pOwner);
   186 }
   187 
   188 unsigned int CRadioStyle::GetMaxPageItems()
   189 {
   190 	return 10;
   191 }
   192 
   193 const char *CRadioStyle::GetStyleName()
   194 {
   195 	return "radio";
   196 }
   197 
   198 CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
   199 {
   200 	return &m_players[client];
   201 }
   202 
   203 CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
   204 {
   205 	CRadioDisplay *display;
   206 	if (m_FreeDisplays.empty())
   207 	{
   208 		display = new CRadioDisplay();
   209 	} else {
   210 		display = m_FreeDisplays.front();
   211 		m_FreeDisplays.pop();
   212 		display->Reset();
   213 	}
   214 	return display;
   215 }
   216 
   217 void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
   218 {
   219 	m_FreeDisplays.push(display);
   220 }
   221 
   222 CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client)
   223 {
   224 	return &m_players[client];
   225 }
   226 
   227 void CRadioStyle::ProcessWatchList()
   228 {
   229 	if (!g_RadioMenuTimeout)
   230 	{
   231 		BaseMenuStyle::ProcessWatchList();
   232 		return;
   233 	}
   234 
   235 	BaseMenuStyle::ProcessWatchList();
   236 
   237 	CRadioMenuPlayer *pPlayer;
   238 	float curtime = gpGlobals->curtime;
   239 	unsigned int max_clients = g_Players.GetMaxClients();
   240 	for (unsigned int i = 1; i <= max_clients; i++)
   241 	{
   242 		pPlayer = GetRadioMenuPlayer(i);
   243 		if (!pPlayer->bInMenu || pPlayer->bInExternMenu)
   244 		{
   245 			continue;
   246 		}
   247 		if (pPlayer->Radio_NeedsRefresh())
   248 		{
   249 			pPlayer->Radio_Refresh();
   250 		}
   251 	}
   252 }
   253 
   254 CRadioDisplay::CRadioDisplay()
   255 {
   256 	Reset();
   257 }
   258 
   259 CRadioDisplay::CRadioDisplay(CRadioMenu *menu)
   260 {
   261 	Reset();
   262 }
   263 
   264 bool CRadioDisplay::DrawRawLine(const char *rawline)
   265 {
   266 	m_BufferText.append(rawline);
   267 	m_BufferText.append("\n");
   268 	return true;
   269 }
   270 
   271 void CRadioDisplay::Reset()
   272 {
   273 	m_BufferText.assign("");
   274 	m_Title.assign("");
   275 	m_NextPos = 1;
   276 	keys = 0;
   277 }
   278 
   279 unsigned int CRadioDisplay::GetCurrentKey()
   280 {
   281 	return m_NextPos;
   282 }
   283 
   284 bool CRadioDisplay::SetCurrentKey(unsigned int key)
   285 {
   286 	if (key < m_NextPos || m_NextPos > 10)
   287 	{
   288 		return false;
   289 	}
   290 
   291 	m_NextPos = key;
   292 
   293 	return true;
   294 }
   295 
   296 bool CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
   297 {
   298 	return g_RadioMenuStyle.DoClientMenu(client, this, handler, time);
   299 }
   300 
   301 bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr)
   302 {
   303 	return false;
   304 }
   305 
   306 IMenuStyle *CRadioDisplay::GetParentStyle()
   307 {
   308 	return &g_RadioMenuStyle;
   309 }
   310 
   311 void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
   312 {
   313 	if (m_Title.size() != 0 && onlyIfEmpty)
   314 	{
   315 		return;
   316 	}
   317 	m_Title.assign(text);
   318 }
   319 
   320 unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
   321 {
   322 	if (m_NextPos > 10 || !CanDrawItem(item.style))
   323 	{
   324 		return 0;
   325 	}
   326 
   327 	if (item.style & ITEMDRAW_RAWLINE)
   328 	{
   329 		if (item.style & ITEMDRAW_SPACER)
   330 		{
   331 			m_BufferText.append(" \n");
   332 		} else {
   333 			m_BufferText.append(item.display);
   334 			m_BufferText.append("\n");
   335 		}
   336 		return 0;
   337 	} else if (item.style & ITEMDRAW_SPACER) {
   338 		m_BufferText.append(" \n");
   339 		return m_NextPos++;
   340 	} else if (item.style & ITEMDRAW_NOTEXT) {
   341 		return m_NextPos++;
   342 	}
   343 
   344 	if (item.style & ITEMDRAW_DISABLED)
   345 	{
   346 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   347 		m_BufferText.append(item.display);
   348 		m_BufferText.append("\n");
   349 	} else {
   350 		m_BufferText.append("->");
   351 		m_BufferText.append(g_RadioNumTable[m_NextPos]);
   352 		m_BufferText.append(item.display);
   353 		m_BufferText.append("\n");
   354 		keys |= (1<<(m_NextPos-1));
   355 	}
   356 
   357 	return m_NextPos++;
   358 }
   359 
   360 bool CRadioDisplay::CanDrawItem(unsigned int drawFlags)
   361 {
   362 	if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
   363 	{
   364 		return false;
   365 	}
   366 
   367 	if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL))
   368 	{
   369 		return false;
   370 	}
   371 
   372 	return true;
   373 }
   374 
   375 void CRadioDisplay::SendRawDisplay(int client, unsigned int time)
   376 {
   377 	int _sel_keys = (keys == 0) ? (1<<9) : keys;
   378 	CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client);
   379 	pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str());
   380 	pPlayer->Radio_Refresh();
   381 }
   382 
   383 void CRadioMenuPlayer::Radio_SetIndex(unsigned int index)
   384 {
   385 	m_index = index;
   386 }
   387 
   388 bool CRadioMenuPlayer::Radio_NeedsRefresh()
   389 {
   390 	return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout);
   391 }
   392 
   393 void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
   394 {
   395 	display_len = UTIL_Format(display_pkt, 
   396 		sizeof(display_pkt), 
   397 		"%s\n%s", 
   398 		title,
   399 		text);
   400 	display_keys = keys;
   401 }
   402 
   403 void CRadioMenuPlayer::Radio_Refresh()
   404 {
   405 	cell_t players[1] = {m_index};
   406 	char *ptr = display_pkt;
   407 	char save = 0;
   408 	size_t len = display_len;
   409 	unsigned int time;
   410 
   411 	/* Compute the new time */
   412 	if (menuHoldTime == 0)
   413 	{
   414 		time = 0;
   415 	}
   416 	else
   417 	{
   418 		time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
   419 	}
   420 
   421 	while (true)
   422 	{
   423 		if (len > 240)
   424 		{
   425 			save = ptr[240];
   426 			ptr[240] = '\0';
   427 		}
   428 		bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
   429 		buffer->WriteWord(display_keys);
   430 		buffer->WriteChar(time ? time : -1);
   431 		buffer->WriteByte( (len > 240) ? 1 : 0 );
   432 		buffer->WriteString(ptr);
   433 		g_UserMsgs.EndMessage();
   434 		if (len > 240)
   435 		{
   436 			ptr[240] = save;
   437 			ptr = &ptr[240];
   438 			len -= 240;
   439 		}
   440 		else
   441 		{
   442 			break;
   443 		}
   444 	}
   445 
   446 	display_last_refresh = gpGlobals->curtime;
   447 }
   448 
   449 int CRadioDisplay::GetAmountRemaining()
   450 {
   451 	size_t amt = m_Title.size() + 1 + m_BufferText.size();
   452 	if (amt >= 511)
   453 	{
   454 		return 0;
   455 	}
   456 	return (int)(511 - amt);
   457 }
   458 
   459 void CRadioDisplay::DeleteThis()
   460 {
   461 	delete this;
   462 }
   463 
   464 bool CRadioDisplay::SetSelectableKeys(unsigned int keymap)
   465 {
   466 	keys = (signed)keymap;
   467 	return true;
   468 }
   469 
   470 CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) : 
   471 CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
   472 {
   473 }
   474 
   475 bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
   476 {
   477 	return false;
   478 }
   479 
   480 IMenuPanel *CRadioMenu::CreatePanel()
   481 {
   482 	return g_RadioMenuStyle.MakeRadioDisplay(this);
   483 }
   484 
   485 bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
   486 {
   487 	return DisplayAtItem(client, time, 0, alt_handler);
   488 }
   489 
   490 bool CRadioMenu::DisplayAtItem(int client,
   491 							   unsigned int time,
   492 							   unsigned int start_item,
   493 							   IMenuHandler *alt_handler)
   494 {
   495 #if defined MENU_DEBUG
   496 	g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
   497 		this,
   498 		client,
   499 		time);
   500 #endif
   501 	if (m_bCancelling)
   502 	{
   503 		return false;
   504 	}
   505 
   506 	return g_RadioMenuStyle.DoClientMenu(client,
   507 		this,
   508 		start_item,
   509 		alt_handler ? alt_handler : m_pHandler, 
   510 		time);
   511 }
   512 
   513 void CRadioMenu::Cancel_Finally()
   514 {
   515 	g_RadioMenuStyle.CancelMenu(this);
   516 }
   517 
   518 const char *g_RadioNumTable[11] = 
   519 {
   520 	"0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. "
   521 };