diff -r fd84073f91e2 -r d7dc79f1f93e core/MenuManager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/MenuManager.cpp Sat May 12 23:45:52 2007 +0000 @@ -0,0 +1,676 @@ +#include +#include +#include "MenuManager.h" +#include "sm_stringutil.h" +#include "sourcemm_api.h" +#include "PlayerManager.h" +#include "MenuStyle_Valve.h" + +MenuManager g_Menus; + +/************************************* + ************************************* + **** BROADCAST HANDLING WRAPPERS **** + ************************************* + *************************************/ + +BroadcastHandler::BroadcastHandler(IMenuHandler *handler) : m_pHandler(handler), numClients(0) +{ +} + +unsigned int BroadcastHandler::GetMenuAPIVersion2() +{ + return m_pHandler->GetMenuAPIVersion2(); +} + +void BroadcastHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason) +{ + m_pHandler->OnMenuCancel(menu, client, reason); +} + +void BroadcastHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuDisplay *display) +{ + numClients++; + m_pHandler->OnMenuDisplay(menu, client, display); +} + +void BroadcastHandler::OnBroadcastEnd(IBaseMenu *menu) +{ + g_Menus.FreeBroadcastHandler(this); +} + +void BroadcastHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item) +{ + m_pHandler->OnMenuSelect(menu, client, item); +} + +void BroadcastHandler::OnMenuEnd(IBaseMenu *menu) +{ + assert(numClients > 0); + + /* Only fire if all clients have gotten a menu end */ + if (--numClients == 0) + { + IMenuHandler *pHandler = m_pHandler; + OnBroadcastEnd(menu); + pHandler->OnMenuEnd(menu); + } +} + +VoteHandler::VoteHandler(IMenuVoteHandler *handler) +: BroadcastHandler(handler), m_pVoteHandler(handler) +{ +} + +void VoteHandler::Initialize(IBaseMenu *menu) +{ + unsigned int numItems = menu->GetItemCount(); + + if (m_counts.size() >= numItems) + { + for (size_t i=0; i m_counts[highest]) + { + m_ties.clear(); + highest = i; + } else if (m_counts[i] == m_counts[highest]) { + m_ties.push_back(i); + } + } + + if (m_ties.size()) + { + m_ties.push_back(highest); + srand(static_cast(time(NULL))); + highest = m_ties[rand() % m_ties.size()]; + } + + m_pVoteHandler->OnMenuVoteEnd(menu, highest); + + g_Menus.FreeVoteHandler(this); +} + +/******************************* + ******************************* + ******** MENU MANAGER ********* + ******************************* + *******************************/ + +MenuManager::MenuManager() +{ + m_ShowMenu = -1; + m_pDefaultStyle = NULL; +} + +void MenuManager::OnSourceModAllInitialized() +{ + int num = g_SMAPI->GetUserMessageCount(); + if (num >= 1) + { + for (int i=0; iGetUserMessage(i, NULL), "ShowMenu") == 0) + { + m_ShowMenu = i; + break; + } + } + } + + /* :TODO: styles */ + m_Styles.push_back(&g_ValveMenuStyle); + SetDefaultStyle(&g_ValveMenuStyle); +} + +void MenuManager::OnSourceModAllShutdown() +{ + while (!m_BroadcastHandlers.empty()) + { + delete m_BroadcastHandlers.front(); + m_BroadcastHandlers.pop(); + } + + while (!m_VoteHandlers.empty()) + { + delete m_VoteHandlers.front(); + m_VoteHandlers.pop(); + } +} + +bool MenuManager::SetDefaultStyle(IMenuStyle *style) +{ + if (!style) + { + return false; + } + + m_pDefaultStyle = style; + + return true; +} + +IMenuStyle *MenuManager::GetStyle(unsigned int index) +{ + if (index >= GetStyleCount()) + { + return NULL; + } + + return m_Styles[index]; +} + +unsigned int MenuManager::GetStyleCount() +{ + return (unsigned int)m_Styles.size(); +} + +IMenuStyle *MenuManager::FindStyleByName(const char *name) +{ + unsigned int count = GetStyleCount(); + for (unsigned int i=0; iGetStyleName(), name) == 0) + { + return ptr; + } + } + + return NULL; +} + +inline bool IsSlotItem(IMenuDisplay *display, + unsigned int style) +{ + if (!display->CanDrawItem(style)) + { + return false; + } + if ((style & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE) + { + return false; + } + if (style & ITEMDRAW_RAWLINE) + { + return false; + } + + return true; +} + +IMenuDisplay *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder order) +{ + IBaseMenu *menu = md.menu; + + if (!menu) + { + return NULL; + } + + struct + { + unsigned int position; + ItemDrawInfo draw; + } drawItems[10]; + + /* Figure out how many items to draw */ + IMenuStyle *style = menu->GetDrawStyle(); + unsigned int pgn = menu->GetPagination(); + unsigned int maxItems = style->GetMaxPageItems(); + if (pgn != MENU_NO_PAGINATION) + { + maxItems = pgn; + } + + unsigned int totalItems = menu->GetItemCount(); + unsigned int startItem = 0; + + /* For pagination, find the starting point. */ + if (pgn != MENU_NO_PAGINATION) + { + if (order == ItemOrder_Ascending) + { + startItem = md.lastItem; + /* This shouldn't happen with well-coded menus. + * If the item is out of bounds, switch the order to + * Items_Descending and make us start from the top. + */ + if (startItem >= totalItems) + { + startItem = totalItems - 1; + order = ItemOrder_Descending; + } + } else if (order == ItemOrder_Descending) { + startItem = md.firstItem; + /* This shouldn't happen with well-coded menus. + * If searching backwards doesn't give us enough room, + * start from the beginning and change to ascending. + */ + if (startItem <= maxItems) + { + startItem = 0; + order = ItemOrder_Ascending; + } + } + } + + /* Get our Display pointer and initialize some crap */ + IMenuDisplay *display = menu->CreateDisplay(); + IMenuHandler *mh = md.mh; + bool foundExtra = false; + unsigned int extraItem = 0; + + /** + * We keep searching until: + * 1) There are no more items + * 2) We reach one OVER the maximum number of slot items + * 3) We have reached maxItems and pagination is MENU_NO_PAGINATION + */ + unsigned int i = startItem; + unsigned int foundItems = 0; + while (true) + { + ItemDrawInfo &dr = drawItems[foundItems].draw; + /* Is the item valid? */ + if (menu->GetItemInfo(i, &dr) != NULL) + { + /* Ask the user to change the style, if necessary */ + mh->OnMenuDrawItem(menu, client, i, dr.style); + /* Check if it's renderable */ + if (IsSlotItem(display, dr.style)) + { + /* If we've already found the max number of items, + * This means we should just cancel out and log our + * "last item." + */ + if (foundItems >= maxItems) + { + foundExtra = true; + extraItem = i; + break; + } + drawItems[foundItems++].position = i; + } + } + /* If there's no pagination, stop once the menu is full. */ + if (pgn == MENU_NO_PAGINATION) + { + /* If we've filled up, then stop */ + if (foundItems >= maxItems) + { + break; + } + } + /* If we're descending and this is the first item, stop */ + if (order == ItemOrder_Descending) + { + if (i == 0) + { + break; + } + i--; + } + /* If we're ascending and this is the last item, stop */ + else if (order == ItemOrder_Ascending) + { + if (i >= totalItems - 1) + { + break; + } + i++; + } + } + + /* There were no items to draw! */ + if (!foundItems) + { + delete display; + return NULL; + } + + /* Check initial buttons */ + bool displayPrev = false; + bool displayNext = false; + if (foundExtra) + { + if (order == ItemOrder_Descending) + { + displayPrev = true; + md.firstItem = extraItem; + } else if (order == ItemOrder_Ascending) { + displayNext = true; + md.lastItem = extraItem; + } + } + + /** + * If we're paginated, we have to find if there is another page. + * Sadly, the only way to do this is to try drawing more items! + */ + if (pgn != MENU_NO_PAGINATION) + { + unsigned int lastItem = 0; + ItemDrawInfo dr; + /* Find the last feasible item to search from. */ + if (order == ItemOrder_Descending) + { + lastItem = drawItems[0].position; + if (lastItem >= totalItems - 1) + { + goto skip_search; + } + while (++lastItem < totalItems) + { + if (menu->GetItemInfo(lastItem, &dr) != NULL) + { + mh->OnMenuDrawItem(menu, client, lastItem, dr.style); + if (IsSlotItem(display, dr.style)) + { + displayNext = true; + md.lastItem = lastItem; + break; + } + } + } + } else if (order == ItemOrder_Ascending) { + lastItem = drawItems[0].position; + if (lastItem == 0) + { + goto skip_search; + } + lastItem--; + while (lastItem-- != 0) + { + if (menu->GetItemInfo(lastItem, &dr) != NULL) + { + mh->OnMenuDrawItem(menu, client, lastItem, dr.style); + if (IsSlotItem(display, dr.style)) + { + displayPrev = true; + md.firstItem = lastItem; + break; + } + } + } + } + } +skip_search: + + /* Draw the item according to the order */ + menu_slots_t *slots = md.slots; + unsigned int position = 0; /* Keep track of the last position */ + if (order == ItemOrder_Ascending) + { + for (unsigned int i=0; iGetItemInfo(drawItems[i].position, &dr); + mh->OnMenuDisplayItem(menu, client, drawItems[i].position, &(dr.display)); + if ((position = display->DrawItem(dr)) != 0) + { + slots[position].item = drawItems[i].position; + slots[position].type = ItemSel_Item; + } + } + } else if (order == ItemOrder_Descending) { + unsigned int i = foundItems; + /* NOTE: There will always be at least one item because + * of the check earlier. + */ + while (i--) + { + ItemDrawInfo &dr = drawItems[i].draw; + menu->GetItemInfo(drawItems[i].position, &dr); + mh->OnMenuDisplayItem(menu, client, drawItems[i].position, &(dr.display)); + if ((position = display->DrawItem(dr)) != 0) + { + slots[position].item = drawItems[i].position; + slots[position].type = ItemSel_Item; + } + } + } + + /* Now, we need to check if we need to add anything extra */ + if (pgn != MENU_NO_PAGINATION) + { + bool canDrawDisabled = display->CanDrawItem(ITEMDRAW_DISABLED); + bool exitButton = menu->GetExitButton(); + char text[50]; + + /* Calculate how many items we are allowed for control stuff */ + unsigned int padding = style->GetMaxPageItems() - maxItems; + + /* Add the number of available slots */ + padding += (maxItems - foundItems); + + /* Someday, if we are able to re-enable this, we will be very lucky men. */ +#if 0 + if (!style->FeatureExists(MenuStyleFeature_ImplicitExit)) + { +#endif + /* Even if we don't draw an exit button, we invalidate the slot. */ + padding--; +#if 0 + } else { + /* Otherwise, we don't draw anything and leave the slot available */ + exitButton = false; + } +#endif + + /* Subtract two slots for the displayNext/displayPrev padding */ + padding -= 2; + + /** + * We allow next/prev to be undrawn if neither exists. + * Thus, we only need padding if one of them will be drawn, + * or the exit button will be drawn. + */ + ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER); + if (exitButton || (displayNext || displayPrev)) + { + /* Add spacers so we can pad to the end */ + unsigned int null_pos = 0; + for (unsigned int i=0; iDrawItem(padItem); + slots[position].type = ItemSel_None; + } + } + ItemDrawInfo dr(text, 0); + + /* PREVIOUS */ + if (displayPrev || canDrawDisabled) + { + CorePlayerTranslate(client, text, sizeof(text), "Back", NULL); + dr.style = displayPrev ? 0 : ITEMDRAW_DISABLED; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Back; + } else if ((displayNext || canDrawDisabled) || exitButton) { + /* If we can't display this, + * but there is a "next" or "exit" button, we need to pad! + */ + position = display->DrawItem(padItem); + slots[position].type = ItemSel_None; + } + + /* NEXT */ + if (displayNext || canDrawDisabled) + { + CorePlayerTranslate(client, text, sizeof(text), "Next", NULL); + dr.style = displayNext ? 0 : ITEMDRAW_DISABLED; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Next; + } else if (exitButton) { + /* If we can't display this, + * but there is an exit button, we need to pad! + */ + position = display->DrawItem(padItem); + slots[position].type = ItemSel_None; + } + + /* EXIT */ + if (exitButton) + { + CorePlayerTranslate(client, text, sizeof(text), "Exit", NULL); + dr.style = 0; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Exit; + } + } + + /* Lastly, fill in any slots we could have missed */ + for (unsigned int i = position + 1; i < 10; i++) + { + slots[i].type = ItemSel_None; + } + + /* Do title stuff */ + mh->OnMenuDisplay(menu, client, display); + display->DrawTitle(menu->GetDefaultTitle(), true); + + return display; +} + +unsigned int MenuManager::BroadcastMenu(IBaseMenu *menu, + IMenuHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time) +{ + BroadcastHandler *bh; + + if (m_BroadcastHandlers.empty()) + { + bh = new BroadcastHandler(handler); + } else { + bh = m_BroadcastHandlers.front(); + m_BroadcastHandlers.pop(); + bh->m_pHandler = handler; + bh->numClients = 0; + } + + handler->OnMenuStart(menu); + + unsigned int total = 0; + for (unsigned int i=0; iDisplay(clients[i], bh, time)) + { + continue; + } + + /* :TODO: Allow sourcetv only, not all bots */ + CPlayer *player = g_Players.GetPlayerByIndex(clients[i]); + if (player->IsFakeClient()) + { + continue; + } + + total++; + } + + if (!total) + { + /* End the broadcast here */ + handler->OnMenuEnd(menu); + FreeBroadcastHandler(bh); + } + + return total; +} + +unsigned int MenuManager::VoteMenu(IBaseMenu *menu, + IMenuVoteHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time) +{ + VoteHandler *vh; + + if (m_VoteHandlers.empty()) + { + vh = new VoteHandler(handler); + } else { + vh = m_VoteHandlers.front(); + m_VoteHandlers.pop(); + vh->m_pHandler = handler; + vh->numClients = 0; + } + + vh->Initialize(menu); + handler->OnMenuStart(menu); + + unsigned int total = 0; + for (unsigned int i=0; iDisplay(clients[i], vh, time)) + { + continue; + } + + /* :TODO: Allow sourcetv only, not all bots */ + CPlayer *player = g_Players.GetPlayerByIndex(clients[i]); + if (player->IsFakeClient()) + { + continue; + } + + total++; + } + + if (!total) + { + /* End the broadcast here */ + handler->OnMenuEnd(menu); + FreeVoteHandler(vh); + } + + return total; +} + +void MenuManager::FreeBroadcastHandler(BroadcastHandler *bh) +{ + m_BroadcastHandlers.push(bh); +} + +void MenuManager::FreeVoteHandler(VoteHandler *vh) +{ + m_VoteHandlers.push(vh); +} + +IMenuStyle *MenuManager::GetDefaultStyle() +{ + return m_pDefaultStyle; +} +