core/MenuStyle_Radio.cpp
author David Anderson <dvander@alliedmods.net>
Wed Mar 12 03:33:52 2008 +0000 (2008-03-12)
changeset 1925 61d7a244b641
parent 1838 68d30bed9612
child 1947 e73a1e51833b
permissions -rw-r--r--
reworked antiflood. it now has some logic in core to take care of loading order nastiness, and to fully prevent trigger spamming
     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 			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 };