core/MenuStyle_Base.cpp
author David Anderson <dvander@alliedmods.net>
Sun May 13 18:55:59 2007 +0000 (2007-05-13)
changeset 787 52283fe61d42
parent 785 bca431afd754
child 790 9b1474d03470
permissions -rw-r--r--
changed IMenuDisplay/CreateDisplay to IMenuPanel/CreatePanel
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <stdarg.h>
    16 #include "sm_stringutil.h"
    17 #include "MenuStyle_Base.h"
    18 #include "PlayerManager.h"
    19 #include "MenuManager.h"
    20 
    21 BaseMenuStyle::BaseMenuStyle() : m_WatchList(256)
    22 {
    23 }
    24 
    25 void BaseMenuStyle::AddClientToWatch(int client)
    26 {
    27 	m_WatchList.push_back(client);
    28 }
    29 
    30 void BaseMenuStyle::RemoveClientFromWatch(int client)
    31 {
    32 	m_WatchList.remove(client);
    33 }
    34 
    35 void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */, MenuCancelReason reason/* =MenuCancel_Interrupt */)
    36 {
    37 	CBaseMenuPlayer *player = GetMenuPlayer(client);
    38 	menu_states_t &states = player->states;
    39 
    40 	bool bOldIgnore = player->bAutoIgnore;
    41 	if (bAutoIgnore)
    42 	{
    43 		player->bAutoIgnore = true;
    44 	}
    45 
    46 	/* Save states */
    47 	IMenuHandler *mh = states.mh;
    48 	IBaseMenu *menu = states.menu;
    49 
    50 	/* Clear menu */
    51 	player->bInMenu = false;
    52 	if (player->menuHoldTime)
    53 	{
    54 		RemoveClientFromWatch(client);
    55 	}
    56 
    57 	/* Fire callbacks */
    58 	mh->OnMenuCancel(menu, client, reason);
    59 	mh->OnMenuEnd(menu);
    60 
    61 	if (bAutoIgnore)
    62 	{
    63 		player->bAutoIgnore = bOldIgnore;
    64 	}
    65 }
    66 
    67 void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
    68 {
    69 	int maxClients = g_Players.GetMaxClients();
    70 	for (int i=1; i<=maxClients; i++)
    71 	{
    72 		CBaseMenuPlayer *player = GetMenuPlayer(i);
    73 		if (player->bInMenu)
    74 		{
    75 			menu_states_t &states = player->states;
    76 			if (states.menu == menu)
    77 			{
    78 				_CancelClientMenu(i);
    79 			}
    80 		}
    81 	}
    82 }
    83 
    84 bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
    85 {
    86 	if (client < 1 || client > g_Players.MaxClients())
    87 	{
    88 		return false;
    89 	}
    90 
    91 	CBaseMenuPlayer *player = GetMenuPlayer(client);
    92 	if (!player->bInMenu)
    93 	{
    94 		return false;
    95 	}
    96 
    97 	_CancelClientMenu(client, autoIgnore);
    98 
    99 	return true;
   100 }
   101 
   102 MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
   103 {
   104 	if (client < 1 || client > g_Players.GetMaxClients())
   105 	{
   106 		return MenuSource_None;
   107 	}
   108 
   109 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   110 
   111 	if (player->bInMenu)
   112 	{
   113 		IBaseMenu *menu;
   114 		if ((menu=player->states.menu) != NULL)
   115 		{
   116 			if (object)
   117 			{
   118 				*object = menu;
   119 			}
   120 			return MenuSource_BaseMenu;
   121 		}
   122 
   123 		return MenuSource_Display;
   124 	} else if (player->bInExternMenu) {
   125 		if (player->menuHoldTime != 0
   126 			&& (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
   127 		{
   128 			player->bInExternMenu = false;
   129 			return MenuSource_None;
   130 		}
   131 		return MenuSource_External;
   132 	}
   133 
   134 	return MenuSource_None;
   135 }
   136 
   137 void BaseMenuStyle::OnClientDisconnected(int client)
   138 {
   139 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   140 	if (!player->bInMenu)
   141 	{
   142 		return;
   143 	}
   144 
   145 	_CancelClientMenu(client, true, MenuCancel_Disconnect);
   146 
   147 	player->bInMenu = false;
   148 }
   149 
   150 static int do_lookup[256];
   151 void BaseMenuStyle::ProcessWatchList()
   152 {
   153 	if (!m_WatchList.size())
   154 	{
   155 		return;
   156 	}
   157 
   158 	unsigned int total = 0;
   159 	for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
   160 	{
   161 		do_lookup[total++] = (*iter);
   162 	}
   163 
   164 	int client;
   165 	CBaseMenuPlayer *player;
   166 	float curTime = gpGlobals->curtime;
   167 	for (unsigned int i=0; i<total; i++)
   168 	{
   169 		client = do_lookup[i];
   170 		player = GetMenuPlayer(client);
   171 		if (!player->bInMenu || !player->menuHoldTime)
   172 		{
   173 			m_WatchList.remove(i);
   174 			continue;
   175 		}
   176 		if (curTime > player->menuStartTime + player->menuHoldTime)
   177 		{
   178 			_CancelClientMenu(client, false);
   179 		}
   180 	}
   181 }
   182 
   183 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
   184 {
   185 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   186 
   187 	/* First question: Are we in a menu? */
   188 	if (!player->bInMenu)
   189 	{
   190 		return;
   191 	}
   192 
   193 	bool cancel = false;
   194 	unsigned int item = 0;
   195 	MenuCancelReason reason = MenuCancel_Exit;
   196 	menu_states_t &states = player->states;
   197 
   198 	assert(states.mh != NULL);
   199 
   200 	if (states.menu == NULL)
   201 	{
   202 		item = key_press;
   203 	} else if (key_press < 1 || key_press > GetMaxPageItems()) {
   204 		cancel = true;
   205 	} else {
   206 		ItemSelection type = states.slots[key_press].type;
   207 
   208 		/* For navigational items, we're going to redisplay */
   209 		if (type == ItemSel_Back)
   210 		{
   211 			if (!RedoClientMenu(client, ItemOrder_Descending))
   212 			{
   213 				cancel = true;
   214 				reason = MenuCancel_NoDisplay;
   215 			} else {
   216 				return;
   217 			}
   218 		} else if (type == ItemSel_Next) {
   219 			if (!RedoClientMenu(client, ItemOrder_Ascending))
   220 			{
   221 				cancel = true;						/* I like Saltines. */
   222 				reason = MenuCancel_NoDisplay;
   223 			} else {
   224 				return;
   225 			}
   226 		} else if (type == ItemSel_Exit || type == ItemSel_None) {
   227 			cancel = true;
   228 		} else {
   229 			item = states.slots[key_press].item;
   230 		}
   231 	}
   232 
   233 	/* Save variables */
   234 	IMenuHandler *mh = states.mh;
   235 	IBaseMenu *menu = states.menu;
   236 
   237 	/* Clear states */
   238 	player->bInMenu = false;
   239 	if (player->menuHoldTime)
   240 	{
   241 		m_WatchList.remove(client);
   242 	}
   243 
   244 	if (cancel)
   245 	{
   246 		mh->OnMenuCancel(menu, client, reason);
   247 	} else {
   248 		mh->OnMenuSelect(menu, client, item);
   249 	}
   250 
   251 	mh->OnMenuEnd(menu);
   252 }
   253 
   254 bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
   255 {
   256 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   257 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   258 	{
   259 		return false;
   260 	}
   261 
   262 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   263 	if (player->bAutoIgnore)
   264 	{
   265 		return false;
   266 	}
   267 
   268 	/* For the duration of this, we are going to totally ignore whether
   269 	* the player is already in a menu or not (except to cancel the old one).
   270 	* Instead, we are simply going to ignore any further menu displays, so
   271 	* this display can't be interrupted.
   272 	*/
   273 	player->bAutoIgnore = true;
   274 
   275 	/* Cancel any old menus */
   276 	menu_states_t &states = player->states;
   277 	if (player->bInMenu)
   278 	{
   279 		/* We need to cancel the old menu */
   280 		if (player->menuHoldTime)
   281 		{
   282 			RemoveClientFromWatch(client);
   283 		}
   284 		states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
   285 		states.mh->OnMenuEnd(states.menu);
   286 	}
   287 
   288 	states.firstItem = 0;
   289 	states.lastItem = 0;
   290 	states.menu = NULL;
   291 	states.mh = mh;
   292 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   293 	player->bInMenu = true;
   294 	player->menuStartTime = gpGlobals->curtime;
   295 	player->menuHoldTime = time;
   296 
   297 	if (time)
   298 	{
   299 		AddClientToWatch(client);
   300 	}
   301 
   302 	/* Draw the display */
   303 	SendDisplay(client, menu);
   304 
   305 	/* We can be interrupted again! */
   306 	player->bAutoIgnore = false;
   307 
   308 	return true;
   309 }
   310 
   311 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
   312 {
   313 	mh->OnMenuStart(menu);
   314 
   315 	if (!mh)
   316 	{
   317 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   318 		mh->OnMenuEnd(menu);
   319 		return false;
   320 	}
   321 
   322 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   323 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   324 	{
   325 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   326 		mh->OnMenuEnd(menu);
   327 		return false;
   328 	}
   329 
   330 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   331 	if (player->bAutoIgnore)
   332 	{
   333 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   334 		mh->OnMenuEnd(menu);
   335 		return false;
   336 	}
   337 
   338 	/* For the duration of this, we are going to totally ignore whether
   339 	* the player is already in a menu or not (except to cancel the old one).
   340 	* Instead, we are simply going to ignore any further menu displays, so
   341 	* this display can't be interrupted.
   342 	*/
   343 	player->bAutoIgnore = true;
   344 
   345 	/* Cancel any old menus */
   346 	menu_states_t &states = player->states;
   347 	if (player->bInMenu)
   348 	{
   349 		_CancelClientMenu(client, true);
   350 	}
   351 
   352 	states.firstItem = 0;
   353 	states.lastItem = 0;
   354 	states.menu = menu;
   355 	states.mh = mh;
   356 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   357 
   358 	IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
   359 	if (!display)
   360 	{
   361 		player->bAutoIgnore = false;
   362 		player->bInMenu = false;
   363 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   364 		mh->OnMenuEnd(menu);
   365 		return false;
   366 	}
   367 
   368 	/* Finally, set our states */
   369 	player->bInMenu = true;
   370 	player->menuStartTime = gpGlobals->curtime;
   371 	player->menuHoldTime = time;
   372 
   373 	if (time)
   374 	{
   375 		AddClientToWatch(client);
   376 	}
   377 
   378 	/* Draw the display */
   379 	SendDisplay(client, display);
   380 
   381 	/* Free the display pointer */
   382 	display->DeleteThis();
   383 
   384 	/* We can be interrupted again! */
   385 	player->bAutoIgnore = false;
   386 
   387 	return true;
   388 }
   389 
   390 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
   391 {
   392 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   393 	menu_states_t &states = player->states;
   394 
   395 	player->bAutoIgnore = true;
   396 	IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
   397 	if (!display)
   398 	{
   399 		if (player->menuHoldTime)
   400 		{
   401 			m_WatchList.remove(client);
   402 		}
   403 		player->bAutoIgnore = false;
   404 		return false;
   405 	}
   406 
   407 	SendDisplay(client, display);
   408 
   409 	display->DeleteThis();
   410 
   411 	player->bAutoIgnore = false;
   412 
   413 	return true;
   414 }
   415 
   416 CBaseMenu::CBaseMenu(IMenuStyle *pStyle) : 
   417 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), m_bShouldDelete(false), m_bCancelling(false)
   418 {
   419 }
   420 
   421 CBaseMenu::~CBaseMenu()
   422 {
   423 }
   424 
   425 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
   426 {
   427 	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
   428 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   429 	{
   430 		return false;
   431 	}
   432 
   433 	CItem item;
   434 
   435 	item.infoString = m_Strings.AddString(info);
   436 	if (draw.display)
   437 	{
   438 		item.displayString = m_Strings.AddString(draw.display);
   439 	}
   440 	item.style = draw.style;
   441 
   442 
   443 	m_items.push_back(item);
   444 
   445 	return true;
   446 }
   447 
   448 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
   449 {
   450 	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
   451 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   452 	{
   453 		return false;
   454 	}
   455 
   456 	if (position >= m_items.size())
   457 	{
   458 		return false;
   459 	}
   460 
   461 	CItem item;
   462 	item.infoString = m_Strings.AddString(info);
   463 	if (draw.display)
   464 	{
   465 		item.displayString = m_Strings.AddString(draw.display);
   466 	}
   467 	item.style = draw.style;
   468 
   469 	CVector<CItem>::iterator iter = m_items.iterAt(position);
   470 	m_items.insert(iter, item);
   471 
   472 	return true;
   473 }
   474 
   475 bool CBaseMenu::RemoveItem(unsigned int position)
   476 {
   477 	if (position >= m_items.size())
   478 	{
   479 		return false;
   480 	}
   481 
   482 	m_items.erase(m_items.iterAt(position));
   483 
   484 	if (m_items.size() == 0)
   485 	{
   486 		m_Strings.Reset();
   487 	}
   488 
   489 	return true;
   490 }
   491 
   492 void CBaseMenu::RemoveAllItems()
   493 {
   494 	m_items.clear();
   495 	m_Strings.Reset();
   496 }
   497 
   498 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
   499 {
   500 	if (position >= m_items.size())
   501 	{
   502 		return NULL;
   503 	}
   504 
   505 	if (draw)
   506 	{
   507 		draw->display = m_Strings.GetString(m_items[position].displayString);
   508 		draw->style = m_items[position].style;
   509 	}
   510 
   511 	return m_Strings.GetString(m_items[position].infoString);
   512 }
   513 
   514 unsigned int CBaseMenu::GetItemCount()
   515 {
   516 	return m_items.size();
   517 }
   518 
   519 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
   520 {
   521 	if (itemsPerPage > 7)
   522 	{
   523 		return false;
   524 	}
   525 
   526 	m_Pagination = itemsPerPage;
   527 
   528 	return true;
   529 }
   530 
   531 unsigned int CBaseMenu::GetPagination()
   532 {
   533 	return m_Pagination;
   534 }
   535 
   536 IMenuStyle *CBaseMenu::GetDrawStyle()
   537 {
   538 	return m_pStyle;
   539 }
   540 
   541 void CBaseMenu::SetDefaultTitle(const char *message)
   542 {
   543 	m_Title.assign(message);
   544 }
   545 
   546 const char *CBaseMenu::GetDefaultTitle()
   547 {
   548 	return m_Title.c_str();
   549 }
   550 
   551 bool CBaseMenu::GetExitButton()
   552 {
   553 	return m_ExitButton;
   554 }
   555 
   556 bool CBaseMenu::SetExitButton(bool set)
   557 {
   558 	m_ExitButton = set;
   559 	return true;
   560 }
   561 
   562 void CBaseMenu::Cancel()
   563 {
   564 	if (m_bCancelling)
   565 	{
   566 		return;
   567 	}
   568 
   569 	m_bCancelling = true;
   570 	Cancel_Finally();
   571 	m_bCancelling = false;
   572 
   573 	if (m_bShouldDelete)
   574 	{
   575 		delete this;
   576 	}
   577 }
   578 
   579 void CBaseMenu::Destroy()
   580 {
   581 	if (!m_bCancelling || m_bShouldDelete)
   582 	{
   583 		Cancel();
   584 		delete this;
   585 	} else {
   586 		m_bShouldDelete = true;
   587 	}
   588 }
   589