Added check to make sure player are alive to perform sm_slap, sm_burn, sm_slay.
3 * ================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 3.0, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to
21 * link the code of this program (as well as its derivative works) to
22 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
23 * Game MODs that run on software by the Valve Corporation. You must
24 * obey the GNU General Public License in all respects for all other
25 * code used. Additionally, AlliedModders LLC grants this exception
26 * to all derivative works. AlliedModders LLC defines further
27 * exceptions, found in LICENSE.txt (as of this writing, version
28 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
34 #include "sm_stringutil.h"
35 #include "MenuStyle_Base.h"
36 #include "PlayerManager.h"
37 #include "MenuManager.h"
38 #include "HandleSys.h"
39 #include "CellRecipientFilter.h"
40 #if defined MENU_DEBUG
44 BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
48 Handle_t BaseMenuStyle::GetHandle()
50 /* Don't create the handle until we need it */
51 if (m_hHandle == BAD_HANDLE)
53 m_hHandle = g_Menus.CreateStyleHandle(this);
59 void BaseMenuStyle::AddClientToWatch(int client)
61 #if defined MENU_DEBUG
62 g_Logger.LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
64 m_WatchList.push_back(client);
67 void BaseMenuStyle::RemoveClientFromWatch(int client)
69 #if defined MENU_DEBUG
70 g_Logger.LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
72 m_WatchList.remove(client);
75 void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore/* =false */)
77 #if defined MENU_DEBUG
78 g_Logger.LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
80 CBaseMenuPlayer *player = GetMenuPlayer(client);
81 menu_states_t &states = player->states;
83 bool bOldIgnore = player->bAutoIgnore;
86 player->bAutoIgnore = true;
90 IMenuHandler *mh = states.mh;
91 IBaseMenu *menu = states.menu;
94 player->bInMenu = false;
95 if (player->menuHoldTime)
97 RemoveClientFromWatch(client);
101 mh->OnMenuCancel(menu, client, reason);
103 /* Only fire end if there's a valid menu */
106 mh->OnMenuEnd(menu, MenuEnd_Cancelled);
111 player->bAutoIgnore = bOldIgnore;
115 void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
117 #if defined MENU_DEBUG
118 g_Logger.LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
120 int maxClients = g_Players.GetMaxClients();
121 for (int i=1; i<=maxClients; i++)
123 CBaseMenuPlayer *player = GetMenuPlayer(i);
126 menu_states_t &states = player->states;
127 if (states.menu == menu)
129 _CancelClientMenu(i, MenuCancel_Interrupted);
135 bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
137 #if defined MENU_DEBUG
138 g_Logger.LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
140 if (client < 1 || client > g_Players.MaxClients())
145 CBaseMenuPlayer *player = GetMenuPlayer(client);
146 if (!player->bInMenu)
151 _CancelClientMenu(client, MenuCancel_Interrupted, autoIgnore);
156 MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
158 if (client < 1 || client > g_Players.GetMaxClients())
160 return MenuSource_None;
163 CBaseMenuPlayer *player = GetMenuPlayer(client);
168 if ((menu=player->states.menu) != NULL)
174 return MenuSource_BaseMenu;
177 return MenuSource_Display;
178 } else if (player->bInExternMenu) {
179 if (player->menuHoldTime != 0
180 && (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
182 player->bInExternMenu = false;
183 return MenuSource_None;
185 return MenuSource_External;
188 return MenuSource_None;
191 void BaseMenuStyle::OnClientDisconnected(int client)
193 #if defined MENU_DEBUG
194 g_Logger.LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
196 CBaseMenuPlayer *player = GetMenuPlayer(client);
197 if (!player->bInMenu)
202 _CancelClientMenu(client, MenuCancel_Disconnected, true);
204 player->bInMenu = false;
205 player->bInExternMenu = false;
208 static int do_lookup[256];
209 void BaseMenuStyle::ProcessWatchList()
211 if (!m_WatchList.size())
216 #if defined MENU_DEBUG
217 g_Logger.LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)",
219 m_WatchList.m_FirstLink,
220 m_WatchList.m_FreeNodes,
221 m_WatchList.m_LastLink,
222 m_WatchList.m_MaxSize,
223 m_WatchList.m_Nodes);
226 unsigned int total = 0;
227 for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
229 do_lookup[total++] = (*iter);
232 #if defined MENU_DEBUG
235 g_Logger.LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
240 CBaseMenuPlayer *player;
241 float curTime = gpGlobals->curtime;
242 for (unsigned int i=0; i<total; i++)
244 client = do_lookup[i];
245 player = GetMenuPlayer(client);
246 #if defined MENU_DEBUG
247 g_Logger.LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
250 player->menuHoldTime,
252 player->menuStartTime);
254 if (!player->bInMenu || !player->menuHoldTime)
256 #if defined MENU_DEBUG
257 g_Logger.LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
259 m_WatchList.remove(client);
262 if (curTime > player->menuStartTime + player->menuHoldTime)
264 _CancelClientMenu(client, MenuCancel_Timeout, false);
269 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
271 #if defined MENU_DEBUG
272 g_Logger.LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
274 CBaseMenuPlayer *player = GetMenuPlayer(client);
276 /* First question: Are we in a menu? */
277 if (!player->bInMenu)
283 unsigned int item = 0;
284 MenuCancelReason reason = MenuCancel_Exit;
285 MenuEndReason end_reason = MenuEnd_Selected;
286 menu_states_t &states = player->states;
288 assert(states.mh != NULL);
290 if (states.menu == NULL)
293 } else if (key_press < 1 || key_press > GetMaxPageItems()) {
296 ItemSelection type = states.slots[key_press].type;
298 /* Check if we should play a sound about the type */
299 if (g_Menus.MenuSoundsEnabled())
301 CellRecipientFilter filter;
305 filter.Initialize(clients, 1);
307 const char *sound = g_Menus.GetMenuSound(type);
311 edict_t *pEdict = engine->PEntityOfEntIndex(client);
314 ICollideable *pCollideable = pEdict->GetCollideable();
318 const Vector & pos = pCollideable->GetCollisionOrigin();
320 enginesound->EmitSound(filter,
334 /* For navigational items, we're going to redisplay */
335 if (type == ItemSel_Back)
337 if (!RedoClientMenu(client, ItemOrder_Descending))
340 reason = MenuCancel_NoDisplay;
341 end_reason = MenuEnd_Cancelled;
345 } else if (type == ItemSel_Next) {
346 if (!RedoClientMenu(client, ItemOrder_Ascending))
348 cancel = true; /* I like Saltines. */
349 reason = MenuCancel_NoDisplay;
350 end_reason = MenuEnd_Cancelled;
354 } else if (type == ItemSel_Exit || type == ItemSel_None) {
356 reason = MenuCancel_Exit;
357 end_reason = MenuEnd_Exit;
358 } else if (type == ItemSel_ExitBack) {
360 reason = MenuCancel_ExitBack;
361 end_reason = MenuEnd_ExitBack;
363 item = states.slots[key_press].item;
368 IMenuHandler *mh = states.mh;
369 IBaseMenu *menu = states.menu;
372 player->bInMenu = false;
373 if (player->menuHoldTime)
375 RemoveClientFromWatch(client);
380 mh->OnMenuCancel(menu, client, reason);
382 mh->OnMenuSelect(menu, client, item);
385 /* Only fire end for valid menus */
388 mh->OnMenuEnd(menu, end_reason);
392 bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
394 #if defined MENU_DEBUG
395 g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
401 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
402 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
407 CBaseMenuPlayer *player = GetMenuPlayer(client);
408 if (player->bAutoIgnore)
413 /* For the duration of this, we are going to totally ignore whether
414 * the player is already in a menu or not (except to cancel the old one).
415 * Instead, we are simply going to ignore any further menu displays, so
416 * this display can't be interrupted.
418 player->bAutoIgnore = true;
420 /* Cancel any old menus */
421 menu_states_t &states = player->states;
424 _CancelClientMenu(client, MenuCancel_Interrupted, true);
427 states.firstItem = 0;
431 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
432 player->bInMenu = true;
433 player->bInExternMenu = false;
434 player->menuStartTime = gpGlobals->curtime;
435 player->menuHoldTime = time;
439 AddClientToWatch(client);
442 /* Draw the display */
443 SendDisplay(client, menu);
445 /* We can be interrupted again! */
446 player->bAutoIgnore = false;
451 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
453 #if defined MENU_DEBUG
454 g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
460 mh->OnMenuStart(menu);
462 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
463 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
465 #if defined MENU_DEBUG
466 g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
468 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
469 mh->OnMenuEnd(menu, MenuEnd_Cancelled);
473 CBaseMenuPlayer *player = GetMenuPlayer(client);
474 if (player->bAutoIgnore)
476 #if defined MENU_DEBUG
477 g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
479 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
480 mh->OnMenuEnd(menu, MenuEnd_Cancelled);
484 /* For the duration of this, we are going to totally ignore whether
485 * the player is already in a menu or not (except to cancel the old one).
486 * Instead, we are simply going to ignore any further menu displays, so
487 * this display can't be interrupted.
489 player->bAutoIgnore = true;
491 /* Cancel any old menus */
492 menu_states_t &states = player->states;
495 #if defined MENU_DEBUG
496 g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
498 _CancelClientMenu(client, MenuCancel_Interrupted, true);
501 states.firstItem = 0;
505 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
507 IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
510 #if defined MENU_DEBUG
511 g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
513 player->bAutoIgnore = false;
514 player->bInMenu = false;
515 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
516 mh->OnMenuEnd(menu, MenuEnd_Cancelled);
520 /* Finally, set our states */
521 player->bInMenu = true;
522 player->bInExternMenu = false;
523 player->menuStartTime = gpGlobals->curtime;
524 player->menuHoldTime = time;
528 AddClientToWatch(client);
531 /* Draw the display */
532 SendDisplay(client, display);
534 /* Free the display pointer */
535 display->DeleteThis();
537 /* We can be interrupted again! */
538 player->bAutoIgnore = false;
540 #if defined MENU_DEBUG
541 g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
547 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
549 #if defined MENU_DEBUG
550 g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
552 CBaseMenuPlayer *player = GetMenuPlayer(client);
553 menu_states_t &states = player->states;
555 player->bAutoIgnore = true;
556 IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
559 #if defined MENU_DEBUG
560 g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
562 if (player->menuHoldTime)
564 RemoveClientFromWatch(client);
566 player->bAutoIgnore = false;
570 SendDisplay(client, display);
572 display->DeleteThis();
574 player->bAutoIgnore = false;
576 #if defined MENU_DEBUG
577 g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
583 CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
584 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true),
585 m_bShouldDelete(false), m_bCancelling(false), m_pOwner(pOwner ? pOwner : g_pCoreIdent),
586 m_bDeleting(false), m_bWillFreeHandle(false), m_hHandle(BAD_HANDLE), m_pHandler(pHandler),
587 m_pVoteHandler(NULL), m_ExitBackButton(false)
591 CBaseMenu::~CBaseMenu()
593 g_Menus.ReleaseVoteWrapper(m_pVoteHandler);
596 Handle_t CBaseMenu::GetHandle()
600 m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
606 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
608 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
609 && m_items.size() >= m_pStyle->GetMaxPageItems())
616 item.infoString = m_Strings.AddString(info);
619 item.displayString = m_Strings.AddString(draw.display);
621 item.style = draw.style;
622 item.access = draw.access;
625 m_items.push_back(item);
630 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
632 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
633 && m_items.size() >= m_pStyle->GetMaxPageItems())
638 if (position >= m_items.size())
644 item.infoString = m_Strings.AddString(info);
647 item.displayString = m_Strings.AddString(draw.display);
649 item.style = draw.style;
650 item.access = draw.access;
652 CVector<CItem>::iterator iter = m_items.iterAt(position);
653 m_items.insert(iter, item);
658 bool CBaseMenu::RemoveItem(unsigned int position)
660 if (position >= m_items.size())
665 m_items.erase(m_items.iterAt(position));
667 if (m_items.size() == 0)
675 void CBaseMenu::RemoveAllItems()
681 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
683 if (position >= m_items.size())
690 draw->display = m_Strings.GetString(m_items[position].displayString);
691 draw->style = m_items[position].style;
694 return m_Strings.GetString(m_items[position].infoString);
697 unsigned int CBaseMenu::GetItemCount()
699 return m_items.size();
702 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
704 if (itemsPerPage > 7 || itemsPerPage == 1)
709 m_Pagination = itemsPerPage;
714 unsigned int CBaseMenu::GetPagination()
719 IMenuStyle *CBaseMenu::GetDrawStyle()
724 void CBaseMenu::SetDefaultTitle(const char *message)
726 m_Title.assign(message);
729 const char *CBaseMenu::GetDefaultTitle()
731 return m_Title.c_str();
734 bool CBaseMenu::GetExitButton()
739 bool CBaseMenu::SetExitButton(bool set)
745 void CBaseMenu::Cancel()
752 #if defined MENU_DEBUG
753 g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_pVote %p) (inVote %d) (m_bShouldDelete %d)",
756 m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false,
760 if (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress())
762 m_pVoteHandler->CancelVoting();
765 m_bCancelling = true;
767 m_bCancelling = false;
775 void CBaseMenu::Destroy(bool releaseHandle)
777 /* Check if we shouldn't be here */
783 #if defined MENU_DEBUG
784 g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
791 /* Save the destruction hint about our handle */
792 m_bWillFreeHandle = releaseHandle;
794 /* Now actually do stuff */
795 if (!m_bCancelling || m_bShouldDelete)
800 m_bShouldDelete = true;
804 void CBaseMenu::InternalDelete()
806 if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
808 Handle_t hndl = m_hHandle;
811 sec.pOwner = m_pOwner;
812 sec.pIdentity = g_pCoreIdent;
814 m_hHandle = BAD_HANDLE;
816 g_HandleSys.FreeHandle(hndl, &sec);
819 m_pHandler->OnMenuDestroy(this);
824 bool CBaseMenu::BroadcastVote(int clients[],
825 unsigned int numClients,
826 unsigned int maxTime,
829 #if defined MENU_DEBUG
830 g_Logger.LogMessage("[SM_MENU] CBaseMenu::BroadcastVote(%p) (maxTime %d) (numClients %d) (m_pVote %p) (inVote %d)",
835 m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false);
839 m_pVoteHandler = g_Menus.CreateVoteWrapper(m_pHandler);
840 } else if (m_pVoteHandler->IsVoteInProgress()) {
844 m_pVoteHandler->InitializeVoting(this);
846 for (unsigned int i=0; i<numClients; i++)
848 VoteDisplay(clients[i], maxTime);
851 m_pVoteHandler->StartVoting();
856 bool CBaseMenu::IsVoteInProgress()
858 return (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress());
861 bool CBaseMenu::GetExitBackButton()
863 return m_ExitBackButton;
866 void CBaseMenu::SetExitBackButton(bool set)
868 m_ExitBackButton = set;