Triggering build.
3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
32 #include "MenuStyle_Radio.h"
33 #include "sm_stringutil.h"
34 #include "UserMessages.h"
35 #include <IGameConfigs.h>
36 #include "PlayerManager.h"
37 #if defined MENU_DEBUG
40 #include "logic_bridge.h"
42 #ifdef USE_PROTOBUF_USERMESSAGES
43 #include <google/protobuf/descriptor.h>
46 #if SOURCE_ENGINE == SE_CSGO
47 #include <game/shared/csgo/protobuf/cstrike15_usermessages.pb.h>
50 extern const char *g_RadioNumTable[];
51 CRadioStyle g_RadioMenuStyle;
52 int g_ShowMenuId = -1;
53 bool g_bRadioInit = false;
54 unsigned int g_RadioMenuTimeout = 0;
57 #define MAX_PAGINATION_OPTIONS 3
59 #define MAX_MENUSLOT_KEYS 10
61 static unsigned int s_RadioMaxPageItems = MAX_MENUSLOT_KEYS;
64 CRadioStyle::CRadioStyle()
66 m_players = new CRadioMenuPlayer[256+1];
67 for (size_t i = 0; i < 256+1; i++)
69 m_players[i].Radio_SetIndex(i);
73 void CRadioStyle::OnSourceModAllInitialized()
75 g_Players.AddClientListener(this);
78 void CRadioStyle::OnSourceModLevelChange(const char *mapName)
86 const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg");
87 if (!msg || msg[0] == '\0')
92 g_ShowMenuId = g_UserMsgs.GetMessageIndex(msg);
99 const char *val = g_pGameConf->GetKeyValue("RadioMenuTimeout");
102 g_RadioMenuTimeout = atoi(val);
106 g_RadioMenuTimeout = 0;
109 const char *items = g_pGameConf->GetKeyValue("RadioMenuMaxPageItems");
112 int value = atoi(items);
114 // Only override the mostly-safe default if it's a sane value
115 if (value > MAX_PAGINATION_OPTIONS && value <= MAX_MENUSLOT_KEYS)
117 s_RadioMaxPageItems = value;
121 g_Menus.AddStyle(this);
122 g_Menus.SetDefaultStyle(this);
124 g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
127 void CRadioStyle::OnSourceModShutdown()
129 g_Players.RemoveClientListener(this);
130 g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false);
132 while (!m_FreeDisplays.empty())
134 delete m_FreeDisplays.front();
135 m_FreeDisplays.pop();
139 bool CRadioStyle::IsSupported()
141 #if SOURCE_ENGINE == SE_DOTA
144 return (g_ShowMenuId != -1);
148 bool CRadioStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd)
150 if (strcmp(cmdname, "menuselect") == 0)
152 if (!m_players[client].bInMenu)
154 m_players[client].bInExternMenu = false;
158 int arg = atoi(cmd.Arg(1));
159 ClientPressedKey(client, arg);
166 static unsigned int g_last_holdtime = 0;
167 static unsigned int g_last_client_count = 0;
168 static int g_last_clients[256];
170 #ifdef USE_PROTOBUF_USERMESSAGES
171 void CRadioStyle::OnUserMessage(int msg_id, protobuf::Message &msg, IRecipientFilter *pFilter)
173 void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
176 #if SOURCE_ENGINE != SE_DOTA
177 int count = pFilter->GetRecipientCount();
179 #ifdef USE_PROTOBUF_USERMESSAGES
180 int c = ((CCSUsrMsg_ShowMenu &)msg).display_time();
182 bf_read br(bf->GetBasePointer(), 3);
185 int c = br.ReadChar();
188 g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
190 for (int i=0; i<count; i++)
192 g_last_clients[g_last_client_count++] = pFilter->GetRecipientIndex(i);
197 void CRadioStyle::OnUserMessageSent(int msg_id)
199 for (unsigned int i=0; i<g_last_client_count; i++)
201 int client = g_last_clients[i];
202 #if defined MENU_DEBUG
203 g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
205 m_players[client].bInExternMenu);
207 if (m_players[client].bInMenu)
209 _CancelClientMenu(client, MenuCancel_Interrupted, true);
211 m_players[client].bInExternMenu = true;
212 m_players[client].menuHoldTime = g_last_holdtime;
214 g_last_client_count = 0;
217 void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
219 CRadioDisplay *rDisplay = (CRadioDisplay *)display;
220 rDisplay->SendRawDisplay(client, m_players[client].menuHoldTime);
223 IMenuPanel *CRadioStyle::CreatePanel()
225 return g_RadioMenuStyle.MakeRadioDisplay();
228 IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
230 return new CRadioMenu(pHandler, pOwner);
233 unsigned int CRadioStyle::GetMaxPageItems()
235 return s_RadioMaxPageItems;
238 const char *CRadioStyle::GetStyleName()
243 CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
245 return &m_players[client];
248 CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
250 CRadioDisplay *display;
251 if (m_FreeDisplays.empty())
253 display = new CRadioDisplay();
257 display = m_FreeDisplays.front();
258 m_FreeDisplays.pop();
264 IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys)
266 CRadioDisplay *pPanel = MakeRadioDisplay(NULL);
268 pPanel->DirectSet(str, keys);
273 void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
275 m_FreeDisplays.push(display);
278 CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client)
280 return &m_players[client];
283 void CRadioStyle::ProcessWatchList()
285 if (!g_RadioMenuTimeout)
287 BaseMenuStyle::ProcessWatchList();
291 BaseMenuStyle::ProcessWatchList();
293 CRadioMenuPlayer *pPlayer;
294 unsigned int max_clients = g_Players.GetMaxClients();
295 for (unsigned int i = 1; i <= max_clients; i++)
297 pPlayer = GetRadioMenuPlayer(i);
298 if (!pPlayer->bInMenu || pPlayer->bInExternMenu)
302 if (pPlayer->Radio_NeedsRefresh())
304 pPlayer->Radio_Refresh();
309 unsigned int CRadioStyle::GetApproxMemUsage()
311 return sizeof(CRadioStyle) + (sizeof(CRadioMenuPlayer) * 257);
314 CRadioDisplay::CRadioDisplay()
319 CRadioDisplay::CRadioDisplay(CRadioMenu *menu)
324 bool CRadioDisplay::DrawRawLine(const char *rawline)
326 m_BufferText.append(rawline);
327 m_BufferText.append("\n");
331 void CRadioDisplay::Reset()
333 m_BufferText.assign("");
339 void CRadioDisplay::DirectSet(const char *str, int keymap)
342 m_BufferText.assign(str);
346 unsigned int CRadioDisplay::GetCurrentKey()
351 bool CRadioDisplay::SetCurrentKey(unsigned int key)
353 if (key < m_NextPos || m_NextPos > s_RadioMaxPageItems)
363 bool CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
365 return g_RadioMenuStyle.DoClientMenu(client, this, handler, time);
368 bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr)
373 IMenuStyle *CRadioDisplay::GetParentStyle()
375 return &g_RadioMenuStyle;
378 void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
380 if (m_Title.size() != 0 && onlyIfEmpty)
384 m_Title.assign(text);
387 unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
389 if (m_NextPos > s_RadioMaxPageItems || !CanDrawItem(item.style))
394 if (item.style & ITEMDRAW_RAWLINE)
396 if (item.style & ITEMDRAW_SPACER)
398 m_BufferText.append(" \n");
400 m_BufferText.append(item.display);
401 m_BufferText.append("\n");
404 } else if (item.style & ITEMDRAW_SPACER) {
405 m_BufferText.append(" \n");
407 } else if (item.style & ITEMDRAW_NOTEXT) {
411 if (item.style & ITEMDRAW_DISABLED)
413 m_BufferText.append(g_RadioNumTable[m_NextPos]);
414 m_BufferText.append(item.display);
415 m_BufferText.append("\n");
417 m_BufferText.append("->");
418 m_BufferText.append(g_RadioNumTable[m_NextPos]);
419 m_BufferText.append(item.display);
420 m_BufferText.append("\n");
421 keys |= (1<<(m_NextPos-1));
427 bool CRadioDisplay::CanDrawItem(unsigned int drawFlags)
429 if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
434 if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL))
442 void CRadioDisplay::SendRawDisplay(int client, unsigned int time)
444 int _sel_keys = (keys == 0) ? (1<<9) : keys;
445 CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client);
446 pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str());
447 pPlayer->Radio_Refresh();
450 void CRadioMenuPlayer::Radio_SetIndex(unsigned int index)
455 bool CRadioMenuPlayer::Radio_NeedsRefresh()
457 return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout);
460 void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
462 if (title[0] != '\0')
464 display_len = UTIL_Format(display_pkt,
472 display_len = UTIL_Format(display_pkt,
480 void CRadioMenuPlayer::Radio_Refresh()
482 #if SOURCE_ENGINE != SE_DOTA
483 cell_t players[1] = {m_index};
484 char *ptr = display_pkt;
486 size_t len = display_len;
489 /* Compute the new time */
490 if (menuHoldTime == 0)
496 time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
499 #ifdef USE_PROTOBUF_USERMESSAGES
500 // If or when we need to support multiple games per engine with this, we can switch to reflection
501 // TODO: find what happens past 240 on CS:GO
502 CCSUsrMsg_ShowMenu *msg = (CCSUsrMsg_ShowMenu *)g_UserMsgs.StartProtobufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
503 msg->set_bits_valid_slots(display_keys);
504 msg->set_display_time(time);
505 msg->set_menu_string(ptr);
506 g_UserMsgs.EndMessage();
516 bf_write *buffer = g_UserMsgs.StartBitBufMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
517 buffer->WriteWord(display_keys);
518 buffer->WriteChar(time ? time : -1);
519 buffer->WriteByte( (len > 240) ? 1 : 0 );
520 buffer->WriteString(ptr);
521 g_UserMsgs.EndMessage();
535 display_last_refresh = gpGlobals->curtime;
539 int CRadioDisplay::GetAmountRemaining()
541 size_t amt = m_Title.size() + 1 + m_BufferText.size();
546 return (int)(511 - amt);
549 void CRadioDisplay::DeleteThis()
554 bool CRadioDisplay::SetSelectableKeys(unsigned int keymap)
556 keys = (signed)keymap;
560 unsigned int CRadioDisplay::GetApproxMemUsage()
562 return sizeof(CRadioDisplay)
563 + m_BufferText.size()
567 CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
568 CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
570 m_Pagination = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
573 bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
578 IMenuPanel *CRadioMenu::CreatePanel()
580 return g_RadioMenuStyle.MakeRadioDisplay(this);
583 bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
585 return DisplayAtItem(client, time, 0, alt_handler);
588 bool CRadioMenu::DisplayAtItem(int client,
590 unsigned int start_item,
591 IMenuHandler *alt_handler)
593 #if defined MENU_DEBUG
594 g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
604 return g_RadioMenuStyle.DoClientMenu(client,
607 alt_handler ? alt_handler : m_pHandler,
611 bool CRadioMenu::SetPagination(unsigned int itemsPerPage)
613 const unsigned int maxPerPage = s_RadioMaxPageItems - MAX_PAGINATION_OPTIONS;
614 if (itemsPerPage > maxPerPage)
619 return CBaseMenu::SetPagination(itemsPerPage);
622 void CRadioMenu::Cancel_Finally()
624 g_RadioMenuStyle.CancelMenu(this);
627 unsigned int CRadioMenu::GetApproxMemUsage()
629 return sizeof(CRadioMenu) + GetBaseMemUsage();
632 const char *g_RadioNumTable[11] =
634 "0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. "