1.1 --- a/extensions/topmenus/TopMenu.cpp Mon Nov 19 23:11:10 2007 +0000
1.2 +++ b/extensions/topmenus/TopMenu.cpp Tue Nov 20 02:22:26 2007 +0000
1.3 @@ -1,1105 +1,1119 @@
1.4 -/**
1.5 - * vim: set ts=4 :
1.6 - * =============================================================================
1.7 - * SourceMod Sample Extension
1.8 - * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
1.9 - * =============================================================================
1.10 - *
1.11 - * This program is free software; you can redistribute it and/or modify it under
1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
1.13 - * Free Software Foundation.
1.14 - *
1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 - * details.
1.19 - *
1.20 - * You should have received a copy of the GNU General Public License along with
1.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 - *
1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 - * by the Valve Corporation. You must obey the GNU General Public License in
1.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 - * this exception to all derivative works. AlliedModders LLC defines further
1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 - * or <http://www.sourcemod.net/license.php>.
1.31 - *
1.32 - * Version: $Id$
1.33 - */
1.34 -
1.35 -#include <stdlib.h>
1.36 -#include <stdarg.h>
1.37 -#include "TopMenu.h"
1.38 -
1.39 -struct obj_by_name_t
1.40 -{
1.41 - unsigned int obj_index;
1.42 - char name[64];
1.43 -};
1.44 -
1.45 -int _SortObjectNamesDescending(const void *ptr1, const void *ptr2);
1.46 -unsigned int strncopy(char *dest, const char *src, size_t count);
1.47 -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
1.48 -
1.49 -TopMenu::TopMenu(ITopMenuObjectCallbacks *callbacks)
1.50 -{
1.51 - m_clients = NULL;
1.52 - m_SerialNo = 1;
1.53 - m_pTitle = callbacks;
1.54 - m_max_clients = 0;
1.55 -
1.56 - if (playerhelpers->IsServerActivated())
1.57 - {
1.58 - CreatePlayers(playerhelpers->GetMaxClients());
1.59 - }
1.60 -}
1.61 -
1.62 -TopMenu::~TopMenu()
1.63 -{
1.64 - /* Delete all categories */
1.65 - while (m_Categories.size())
1.66 - {
1.67 - RemoveFromMenu(m_Categories[0]->obj->object_id);
1.68 - }
1.69 -
1.70 - /* Remove all objects */
1.71 - for (size_t i = 0; i < m_Objects.size(); i++)
1.72 - {
1.73 - assert(m_Objects[i]->is_free == true);
1.74 - delete m_Objects[i];
1.75 - }
1.76 -
1.77 - m_pTitle->OnTopMenuObjectRemoved(this, 0);
1.78 -
1.79 - /* Delete all cached config entries */
1.80 - for (size_t i = 0; i < m_Config.cats.size(); i++)
1.81 - {
1.82 - delete m_Config.cats[i];
1.83 - }
1.84 -
1.85 - /* Sweep players */
1.86 - for (size_t i = 0; i <= (size_t)m_max_clients; i++)
1.87 - {
1.88 - TearDownClient(&m_clients[i]);
1.89 - }
1.90 - delete [] m_clients;
1.91 -}
1.92 -
1.93 -void TopMenu::OnClientConnected(int client)
1.94 -{
1.95 - if (m_clients == NULL)
1.96 - {
1.97 - return;
1.98 - }
1.99 -
1.100 - topmenu_player_t *player = &m_clients[client];
1.101 - TearDownClient(player);
1.102 -}
1.103 -
1.104 -void TopMenu::OnClientDisconnected(int client)
1.105 -{
1.106 - if (m_clients == NULL)
1.107 - {
1.108 - return;
1.109 - }
1.110 -
1.111 - topmenu_player_t *player = &m_clients[client];
1.112 - TearDownClient(player);
1.113 -}
1.114 -
1.115 -void TopMenu::OnServerActivated(int max_clients)
1.116 -{
1.117 - if (m_clients == NULL)
1.118 - {
1.119 - CreatePlayers(max_clients);
1.120 - }
1.121 -}
1.122 -
1.123 -unsigned int TopMenu::AddToMenu(const char *name,
1.124 - TopMenuObjectType type,
1.125 - ITopMenuObjectCallbacks *callbacks,
1.126 - IdentityToken_t *owner,
1.127 - const char *cmdname,
1.128 - FlagBits flags,
1.129 - unsigned int parent)
1.130 -{
1.131 - return AddToMenu2(name, type, callbacks, owner, cmdname, flags, parent, NULL);
1.132 -}
1.133 -
1.134 -unsigned int TopMenu::AddToMenu2(const char *name,
1.135 - TopMenuObjectType type,
1.136 - ITopMenuObjectCallbacks *callbacks,
1.137 - IdentityToken_t *owner,
1.138 - const char *cmdname,
1.139 - FlagBits flags,
1.140 - unsigned int parent,
1.141 - const char *info_string)
1.142 -{
1.143 - /* Sanity checks */
1.144 - if (type == TopMenuObject_Category && parent != 0)
1.145 - {
1.146 - return 0;
1.147 - }
1.148 - else if (type == TopMenuObject_Item && parent == 0)
1.149 - {
1.150 - return 0;
1.151 - }
1.152 - else if (m_ObjLookup.retrieve(name) != NULL)
1.153 - {
1.154 - return 0;
1.155 - }
1.156 - else if (type != TopMenuObject_Item && type != TopMenuObject_Category)
1.157 - {
1.158 - return 0;
1.159 - }
1.160 -
1.161 - /* If we're adding an item, make sure the parent is valid,
1.162 - * and that the parent is a category.
1.163 - */
1.164 - topmenu_object_t *parent_obj = NULL;
1.165 - topmenu_category_t *parent_cat = NULL;
1.166 - if (type == TopMenuObject_Item)
1.167 - {
1.168 - /* Check parent index. Note it will be >= 1 here. */
1.169 - if (parent > m_Objects.size() || m_Objects[parent - 1]->is_free)
1.170 - {
1.171 - return 0;
1.172 - }
1.173 - parent_obj = m_Objects[parent - 1];
1.174 -
1.175 - /* Find an equivalent pointer in the category array. */
1.176 - for (size_t i = 0; i < m_Categories.size(); i++)
1.177 - {
1.178 - if (m_Categories[i]->obj == parent_obj)
1.179 - {
1.180 - parent_cat = m_Categories[i];
1.181 - break;
1.182 - }
1.183 - }
1.184 -
1.185 - /* If none was found, leave. */
1.186 - if (parent_cat == NULL)
1.187 - {
1.188 - return 0;
1.189 - }
1.190 - }
1.191 -
1.192 - /* Re-use an old object pointer if we can. */
1.193 - topmenu_object_t *obj = NULL;
1.194 - for (size_t i = 0; i < m_Objects.size(); i++)
1.195 - {
1.196 - if (m_Objects[i]->is_free == true)
1.197 - {
1.198 - obj = m_Objects[i];
1.199 - break;
1.200 - }
1.201 - }
1.202 -
1.203 - /* Otherwise, allocate a new one. */
1.204 - if (obj == NULL)
1.205 - {
1.206 - obj = new topmenu_object_t;
1.207 - obj->object_id = ((unsigned int)m_Objects.size()) + 1;
1.208 - m_Objects.push_back(obj);
1.209 - }
1.210 -
1.211 - /* Initialize the object's properties. */
1.212 - obj->callbacks = callbacks;
1.213 - obj->flags = flags;
1.214 - obj->owner = owner;
1.215 - obj->type = type;
1.216 - obj->is_free = false;
1.217 - obj->parent = parent_obj;
1.218 - strncopy(obj->name, name, sizeof(obj->name));
1.219 - strncopy(obj->cmdname, cmdname ? cmdname : "", sizeof(obj->cmdname));
1.220 - strncopy(obj->info, info_string ? info_string : "", sizeof(obj->info));
1.221 -
1.222 - if (obj->type == TopMenuObject_Category)
1.223 - {
1.224 - /* Create a new category entry */
1.225 - topmenu_category_t *cat = new topmenu_category_t;
1.226 - cat->obj = obj;
1.227 - cat->reorder = false;
1.228 - cat->serial = 1;
1.229 -
1.230 - /* Add it, then update our serial change number. */
1.231 - m_Categories.push_back(cat);
1.232 - m_SerialNo++;
1.233 -
1.234 - /* Updating sorting info */
1.235 - m_bCatsNeedResort = true;
1.236 - }
1.237 - else if (obj->type == TopMenuObject_Item)
1.238 - {
1.239 - /* Update the category, mark it as needing changes */
1.240 - parent_cat->obj_list.push_back(obj);
1.241 - parent_cat->reorder = true;
1.242 - parent_cat->serial++;
1.243 -
1.244 - /* If the category just went from 0 to 1 items, mark it as
1.245 - * changed, so clients get the category drawn.
1.246 - */
1.247 - if (parent_cat->obj_list.size() == 1)
1.248 - {
1.249 - m_SerialNo++;
1.250 - }
1.251 - }
1.252 -
1.253 - m_ObjLookup.insert(name, obj);
1.254 -
1.255 - return obj->object_id;
1.256 -}
1.257 -
1.258 -const char *TopMenu::GetObjectInfoString(unsigned int object_id)
1.259 -{
1.260 - if (object_id == 0
1.261 - || object_id > m_Objects.size()
1.262 - || m_Objects[object_id - 1]->is_free)
1.263 - {
1.264 - return NULL;
1.265 - }
1.266 -
1.267 - topmenu_object_t *obj = m_Objects[object_id - 1];
1.268 -
1.269 - return obj->info;
1.270 -}
1.271 -
1.272 -void TopMenu::RemoveFromMenu(unsigned int object_id)
1.273 -{
1.274 - if (object_id == 0
1.275 - || object_id > m_Objects.size()
1.276 - || m_Objects[object_id - 1]->is_free)
1.277 - {
1.278 - return;
1.279 - }
1.280 -
1.281 - topmenu_object_t *obj = m_Objects[object_id - 1];
1.282 -
1.283 - m_ObjLookup.remove(obj->name);
1.284 -
1.285 - if (obj->type == TopMenuObject_Category)
1.286 - {
1.287 - /* Find it in the category list. */
1.288 - for (size_t i = 0; i < m_Categories.size(); i++)
1.289 - {
1.290 - if (m_Categories[i]->obj == obj)
1.291 - {
1.292 - /* Mark all children as removed + free. Note we could
1.293 - * call into RemoveMenuItem() for this, but it'd be very
1.294 - * inefficient!
1.295 - */
1.296 - topmenu_category_t *cat = m_Categories[i];
1.297 - for (size_t j = 0; j < m_Categories[i]->obj_list.size(); j++)
1.298 - {
1.299 - cat->obj_list[j]->callbacks->OnTopMenuObjectRemoved(this, cat->obj_list[j]->object_id);
1.300 - cat->obj_list[j]->is_free = true;
1.301 - }
1.302 -
1.303 - /* Remove the category from the list, then delete it. */
1.304 - m_Categories.erase(m_Categories.iterAt(i));
1.305 - delete cat;
1.306 - break;
1.307 - }
1.308 - }
1.309 -
1.310 - /* Update the root as changed. */
1.311 - m_SerialNo++;
1.312 - m_bCatsNeedResort = true;
1.313 - }
1.314 - else if (obj->type == TopMenuObject_Item)
1.315 - {
1.316 - /* Find the category this item is in. */
1.317 - topmenu_category_t *parent_cat = NULL;
1.318 - for (size_t i = 0; i < m_Categories.size(); i++)
1.319 - {
1.320 - if (m_Categories[i]->obj == obj->parent)
1.321 - {
1.322 - parent_cat = m_Categories[i];
1.323 - break;
1.324 - }
1.325 - }
1.326 -
1.327 - /* Erase it from the category's lists. */
1.328 - if (parent_cat)
1.329 - {
1.330 - for (size_t i = 0; i < parent_cat->obj_list.size(); i++)
1.331 - {
1.332 - if (parent_cat->obj_list[i] == obj)
1.333 - {
1.334 - parent_cat->obj_list.erase(parent_cat->obj_list.iterAt(i));
1.335 -
1.336 - /* If this category now has no items, mark root as changed
1.337 - * so clients won't get the category drawn anymore.
1.338 - */
1.339 - if (parent_cat->obj_list.size() == 0)
1.340 - {
1.341 - m_SerialNo++;
1.342 - }
1.343 - break;
1.344 - }
1.345 - }
1.346 -
1.347 - /* Update the category as changed. */
1.348 - parent_cat->reorder = true;
1.349 - parent_cat->serial++;
1.350 - }
1.351 - }
1.352 -
1.353 - /* The callbacks pointer is still valid, so fire away! */
1.354 - obj->callbacks->OnTopMenuObjectRemoved(this, object_id);
1.355 -
1.356 - /* Finally, mark the object as free. */
1.357 - obj->is_free = true;
1.358 -}
1.359 -
1.360 -bool TopMenu::DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position)
1.361 -{
1.362 - if (m_clients == NULL)
1.363 - {
1.364 - return false;
1.365 - }
1.366 -
1.367 - IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
1.368 - if (!pPlayer->IsInGame())
1.369 - {
1.370 - return false;
1.371 - }
1.372 -
1.373 - UpdateClientRoot(client, pPlayer);
1.374 -
1.375 - topmenu_player_t *pClient = &m_clients[client];
1.376 - if (pClient->root == NULL)
1.377 - {
1.378 - return false;
1.379 - }
1.380 -
1.381 - bool return_value = false;
1.382 -
1.383 - if (position == TopMenuPosition_LastCategory &&
1.384 - pClient->last_category < m_Categories.size())
1.385 - {
1.386 - return_value = DisplayCategory(client, pClient->last_category, hold_time, true);
1.387 - if (!return_value)
1.388 - {
1.389 - return_value = pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
1.390 - }
1.391 - }
1.392 - else if (position == TopMenuPosition_LastRoot)
1.393 - {
1.394 - pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
1.395 - }
1.396 - else if (position == TopMenuPosition_Start)
1.397 - {
1.398 - pClient->last_position = 0;
1.399 - pClient->last_category = 0;
1.400 - return_value = pClient->root->Display(client, hold_time);
1.401 - }
1.402 -
1.403 - return return_value;
1.404 -}
1.405 -
1.406 -bool TopMenu::DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position)
1.407 -{
1.408 - UpdateClientCategory(client, category);
1.409 -
1.410 - topmenu_player_t *pClient = &m_clients[client];
1.411 - if (category >= pClient->cat_count
1.412 - || pClient->cats[category].menu == NULL)
1.413 - {
1.414 - return false;
1.415 - }
1.416 -
1.417 - bool return_value = false;
1.418 -
1.419 - topmenu_player_category_t *player_cat = &(pClient->cats[category]);
1.420 -
1.421 - pClient->last_category = category;
1.422 - if (last_position)
1.423 - {
1.424 - return_value = player_cat->menu->DisplayAtItem(client, hold_time, pClient->last_position);
1.425 - }
1.426 - else
1.427 - {
1.428 - return_value = player_cat->menu->Display(client, hold_time);
1.429 - }
1.430 -
1.431 - return return_value;
1.432 -}
1.433 -
1.434 -void TopMenu::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
1.435 -{
1.436 - const char *item_name = menu->GetItemInfo(item, NULL);
1.437 - if (!item_name)
1.438 - {
1.439 - return;
1.440 - }
1.441 -
1.442 - topmenu_object_t *obj;
1.443 - topmenu_player_t *pClient = &m_clients[client];
1.444 - topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.445 - if (pObject == NULL)
1.446 - {
1.447 - return;
1.448 - }
1.449 -
1.450 - obj = *pObject;
1.451 -
1.452 - /* We now have the object... what do we do with it? */
1.453 - if (obj->type == TopMenuObject_Category)
1.454 - {
1.455 - /* If it's a category, the user wants to view it.. */
1.456 - for (size_t i = 0; i < m_Categories.size(); i++)
1.457 - {
1.458 - if (m_Categories[i]->obj == obj)
1.459 - {
1.460 - pClient->last_root_pos = item_on_page;
1.461 - if (!DisplayCategory(client, (unsigned int)i, MENU_TIME_FOREVER, false))
1.462 - {
1.463 - /* If we can't display the category, re-display the root menu */
1.464 - DisplayMenu(client, MENU_TIME_FOREVER, TopMenuPosition_LastRoot);
1.465 - }
1.466 - break;
1.467 - }
1.468 - }
1.469 - }
1.470 - else
1.471 - {
1.472 - pClient->last_position = item_on_page;
1.473 -
1.474 - /* Re-check access in case this user had their credentials revoked */
1.475 - if (obj->cmdname[0] != '\0' && !adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
1.476 - {
1.477 - DisplayMenu(client, 0, TopMenuPosition_LastCategory);
1.478 - return;
1.479 - }
1.480 -
1.481 - /* Pass the information on to the callback */
1.482 - obj->callbacks->OnTopMenuSelectOption(this, client, obj->object_id);
1.483 - }
1.484 -}
1.485 -
1.486 -void TopMenu::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
1.487 -{
1.488 - const char *item_name = menu->GetItemInfo(item, NULL);
1.489 - if (!item_name)
1.490 - {
1.491 - return;
1.492 - }
1.493 -
1.494 - topmenu_object_t *obj;
1.495 - topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.496 - if (pObject == NULL)
1.497 - {
1.498 - return;
1.499 - }
1.500 -
1.501 - obj = *pObject;
1.502 -
1.503 - style = obj->callbacks->OnTopMenuDrawOption(this, client, obj->object_id);
1.504 - if (style != ITEMDRAW_DEFAULT)
1.505 - {
1.506 - return;
1.507 - }
1.508 -
1.509 - if (obj->cmdname[0] == '\0')
1.510 - {
1.511 - return;
1.512 - }
1.513 -
1.514 - if (!adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
1.515 - {
1.516 - style = ITEMDRAW_IGNORE;
1.517 - }
1.518 -}
1.519 -
1.520 -unsigned int TopMenu::OnMenuDisplayItem(IBaseMenu *menu,
1.521 - int client,
1.522 - IMenuPanel *panel,
1.523 - unsigned int item,
1.524 - const ItemDrawInfo &dr)
1.525 -{
1.526 - const char *item_name = menu->GetItemInfo(item, NULL);
1.527 - if (!item_name)
1.528 - {
1.529 - return 0;
1.530 - }
1.531 -
1.532 - topmenu_object_t *obj;
1.533 - topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.534 - if (pObject == NULL)
1.535 - {
1.536 - return 0;
1.537 - }
1.538 -
1.539 - obj = *pObject;
1.540 -
1.541 - /* Ask the object to render the text for this client */
1.542 - char renderbuf[64];
1.543 - obj->callbacks->OnTopMenuDisplayOption(this, client, obj->object_id, renderbuf, sizeof(renderbuf));
1.544 -
1.545 - /* Build the new draw info */
1.546 - ItemDrawInfo new_dr = dr;
1.547 - new_dr.display = renderbuf;
1.548 -
1.549 - /* Man I love the menu API. Ask the panel to draw the item and give the position
1.550 - * back to Core's renderer. This way we don't have to worry about keeping the
1.551 - * render buffer static!
1.552 - */
1.553 - return panel->DrawItem(new_dr);
1.554 -}
1.555 -
1.556 -void TopMenu::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
1.557 -{
1.558 - if (reason == MenuCancel_ExitBack)
1.559 - {
1.560 - /* If the client chose exit back, they were on a category menu, and we can
1.561 - * now display the root menu from the last known position.
1.562 - */
1.563 - DisplayMenu(client, 0, TopMenuPosition_LastRoot);
1.564 - }
1.565 -}
1.566 -
1.567 -void TopMenu::UpdateClientRoot(int client, IGamePlayer *pGamePlayer)
1.568 -{
1.569 - topmenu_player_t *pClient = &m_clients[client];
1.570 - IGamePlayer *pPlayer = pGamePlayer ? pGamePlayer : playerhelpers->GetGamePlayer(client);
1.571 -
1.572 - /* Determine if an update is necessary */
1.573 - bool is_update_needed = false;
1.574 - if (pClient->menu_serial != m_SerialNo)
1.575 - {
1.576 - is_update_needed = true;
1.577 - }
1.578 - else if (pPlayer->GetUserId() != pClient->user_id)
1.579 - {
1.580 - is_update_needed = true;
1.581 - }
1.582 -
1.583 - /* If no update is needed at the root level, just leave now */
1.584 - if (!is_update_needed)
1.585 - {
1.586 - return;
1.587 - }
1.588 -
1.589 - /* First we need to flush the cache... */
1.590 - TearDownClient(pClient);
1.591 -
1.592 - /* Now, rebuild the category list, but don't create menus */
1.593 - if (m_Categories.size() == 0)
1.594 - {
1.595 - pClient->cat_count = 0;
1.596 - pClient->cats = NULL;
1.597 - }
1.598 - else
1.599 - {
1.600 - pClient->cat_count = (unsigned int)m_Categories.size();
1.601 - pClient->cats = new topmenu_player_category_t[pClient->cat_count];
1.602 - memset(pClient->cats, 0, sizeof(topmenu_player_category_t) * pClient->cat_count);
1.603 - }
1.604 -
1.605 - /* Re-sort the root categories if needed */
1.606 - SortCategoriesIfNeeded();
1.607 -
1.608 - /* Build the root menu */
1.609 - IBaseMenu *root_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
1.610 -
1.611 - /* Add the sorted items */
1.612 - for (size_t i = 0; i < m_SortedCats.size(); i++)
1.613 - {
1.614 - if (m_Categories[m_SortedCats[i]]->obj_list.size() == 0)
1.615 - {
1.616 - continue;
1.617 - }
1.618 - root_menu->AppendItem(m_Categories[m_SortedCats[i]]->obj->name, ItemDrawInfo(""));
1.619 - }
1.620 -
1.621 - /* Now we need to handle un-sorted items. This is slightly trickier, as we need to
1.622 - * pre-render each category name, and cache those names. Phew!
1.623 - */
1.624 - if (m_UnsortedCats.size())
1.625 - {
1.626 - obj_by_name_t *item_list = new obj_by_name_t[m_UnsortedCats.size()];
1.627 - for (size_t i = 0; i < m_UnsortedCats.size(); i++)
1.628 - {
1.629 - obj_by_name_t *temp_obj = &item_list[i];
1.630 - topmenu_object_t *obj = m_Categories[m_UnsortedCats[i]]->obj;
1.631 - obj->callbacks->OnTopMenuDisplayOption(this,
1.632 - client,
1.633 - obj->object_id,
1.634 - temp_obj->name,
1.635 - sizeof(temp_obj->name));
1.636 - temp_obj->obj_index = m_UnsortedCats[i];
1.637 - }
1.638 -
1.639 - /* Sort our temp list */
1.640 - qsort(item_list, m_UnsortedCats.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
1.641 -
1.642 - /* Add the new sorted categories */
1.643 - for (size_t i = 0; i < m_UnsortedCats.size(); i++)
1.644 - {
1.645 - if (m_Categories[item_list[i].obj_index]->obj_list.size() == 0)
1.646 - {
1.647 - continue;
1.648 - }
1.649 - root_menu->AppendItem(m_Categories[item_list[i].obj_index]->obj->name, ItemDrawInfo(""));
1.650 - }
1.651 -
1.652 - delete [] item_list;
1.653 - }
1.654 -
1.655 - /* Set the menu's title */
1.656 - char renderbuf[128];
1.657 - m_pTitle->OnTopMenuDisplayTitle(this, client, 0, renderbuf, sizeof(renderbuf));
1.658 - root_menu->SetDefaultTitle(renderbuf);
1.659 -
1.660 - /* The client is now fully updated */
1.661 - pClient->root = root_menu;
1.662 - pClient->user_id = pPlayer->GetUserId();
1.663 - pClient->menu_serial = m_SerialNo;
1.664 - pClient->last_position = 0;
1.665 - pClient->last_category = 0;
1.666 - pClient->last_root_pos = 0;
1.667 -}
1.668 -
1.669 -void TopMenu::UpdateClientCategory(int client, unsigned int category)
1.670 -{
1.671 - /* Update the client's root menu just in case it needs it. This
1.672 - * will validate that we have both a valid client and a valid
1.673 - * category structure for that client.
1.674 - */
1.675 - UpdateClientRoot(client);
1.676 -
1.677 - /* Now it's guaranteed that our category tables will be usable */
1.678 - topmenu_player_t *pClient = &m_clients[client];
1.679 - topmenu_category_t *cat = m_Categories[category];
1.680 - topmenu_player_category_t *player_cat = &(pClient->cats[category]);
1.681 -
1.682 - /* Does the category actually need updating? */
1.683 - if (player_cat->serial == cat->serial)
1.684 - {
1.685 - return;
1.686 - }
1.687 -
1.688 - /* Destroy any existing menu */
1.689 - if (player_cat->menu)
1.690 - {
1.691 - player_cat->menu->Destroy();
1.692 - player_cat->menu = NULL;
1.693 - }
1.694 -
1.695 - if (pClient->last_category == category)
1.696 - {
1.697 - pClient->last_position = 0;
1.698 - }
1.699 -
1.700 - IBaseMenu *cat_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
1.701 -
1.702 - /* Categories get an "exit back" button */
1.703 - cat_menu->SetMenuOptionFlags(cat_menu->GetMenuOptionFlags() | MENUFLAG_BUTTON_EXITBACK);
1.704 -
1.705 - /* Re-sort the category if needed */
1.706 - SortCategoryIfNeeded(category);
1.707 -
1.708 - /* Build the menu with the sorted items first */
1.709 - for (size_t i = 0; i < cat->sorted.size(); i++)
1.710 - {
1.711 - cat_menu->AppendItem(cat->sorted[i]->name, ItemDrawInfo(""));
1.712 - }
1.713 -
1.714 - /* Now handle unsorted items */
1.715 - if (cat->unsorted.size())
1.716 - {
1.717 - /* Build a list of the item names */
1.718 - obj_by_name_t *item_list = new obj_by_name_t[cat->unsorted.size()];
1.719 - for (size_t i = 0; i < cat->unsorted.size(); i++)
1.720 - {
1.721 - obj_by_name_t *item = &item_list[i];
1.722 - topmenu_object_t *obj = cat->unsorted[i];
1.723 - obj->callbacks->OnTopMenuDisplayOption(this,
1.724 - client,
1.725 - obj->object_id,
1.726 - item->name,
1.727 - sizeof(item->name));
1.728 - item->obj_index = (unsigned int)i;
1.729 - }
1.730 -
1.731 - /* Sort the names */
1.732 - qsort(item_list, cat->unsorted.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
1.733 -
1.734 - /* Add to the menu */
1.735 - for (size_t i = 0; i < cat->unsorted.size(); i++)
1.736 - {
1.737 - cat_menu->AppendItem(cat->unsorted[item_list[i].obj_index]->name, ItemDrawInfo(""));
1.738 - }
1.739 -
1.740 - delete [] item_list;
1.741 - }
1.742 -
1.743 - /* Set the menu's title */
1.744 - char renderbuf[128];
1.745 - cat->obj->callbacks->OnTopMenuDisplayTitle(this,
1.746 - client,
1.747 - cat->obj->object_id,
1.748 - renderbuf,
1.749 - sizeof(renderbuf));
1.750 - cat_menu->SetDefaultTitle(renderbuf);
1.751 -
1.752 - /* We're done! */
1.753 - player_cat->menu = cat_menu;
1.754 - player_cat->serial = cat->serial;
1.755 -}
1.756 -
1.757 -void TopMenu::SortCategoryIfNeeded(unsigned int category)
1.758 -{
1.759 - topmenu_category_t *cat = m_Categories[category];
1.760 - if (!cat->reorder)
1.761 - {
1.762 - return;
1.763 - }
1.764 -
1.765 - cat->sorted.clear();
1.766 - cat->unsorted.clear();
1.767 -
1.768 - if (cat->obj_list.size() == 0)
1.769 - {
1.770 - cat->reorder = false;
1.771 - return;
1.772 - }
1.773 -
1.774 - CVector<unsigned int> to_sort;
1.775 - for (size_t i = 0; i < cat->obj_list.size(); i++)
1.776 - {
1.777 - to_sort.push_back(i);
1.778 - }
1.779 -
1.780 - /* Find a matching category in the configs */
1.781 - config_category_t *config_cat = NULL;
1.782 - for (size_t i = 0; i < m_Config.cats.size(); i++)
1.783 - {
1.784 - if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), cat->obj->name) == 0)
1.785 - {
1.786 - config_cat = m_Config.cats[i];
1.787 - break;
1.788 - }
1.789 - }
1.790 -
1.791 - /* If there is a matching category, build a pre-sorted item list */
1.792 - if (config_cat != NULL)
1.793 - {
1.794 - /* Find matching objects in this category */
1.795 - for (size_t i = 0; i < config_cat->commands.size(); i++)
1.796 - {
1.797 - const char *config_name = m_Config.strings.GetString(config_cat->commands[i]);
1.798 - for (size_t j = 0; j < to_sort.size(); j++)
1.799 - {
1.800 - if (strcmp(config_name, cat->obj_list[to_sort[j]]->name) == 0)
1.801 - {
1.802 - /* Place in the final list, then remove from the temporary list */
1.803 - cat->sorted.push_back(cat->obj_list[to_sort[j]]);
1.804 - to_sort.erase(to_sort.iterAt(j));
1.805 - break;
1.806 - }
1.807 - }
1.808 - }
1.809 - }
1.810 -
1.811 - /* Push any remaining items onto the unsorted list */
1.812 - for (size_t i = 0; i < to_sort.size(); i++)
1.813 - {
1.814 - cat->unsorted.push_back(cat->obj_list[to_sort[i]]);
1.815 - }
1.816 -
1.817 - cat->reorder = false;
1.818 -}
1.819 -
1.820 -void TopMenu::SortCategoriesIfNeeded()
1.821 -{
1.822 - if (!m_bCatsNeedResort)
1.823 - {
1.824 - return;
1.825 - }
1.826 -
1.827 - /* Clear sort results */
1.828 - m_SortedCats.clear();
1.829 - m_UnsortedCats.clear();
1.830 -
1.831 - if (m_Categories.size() == 0)
1.832 - {
1.833 - m_bCatsNeedResort = false;
1.834 - return;
1.835 - }
1.836 -
1.837 - CVector<unsigned int> to_sort;
1.838 - for (unsigned int i = 0; i < (unsigned int)m_Categories.size(); i++)
1.839 - {
1.840 - to_sort.push_back(i);
1.841 - }
1.842 -
1.843 - /* If we have any predefined categories, add them in as they appear. */
1.844 - for (size_t i= 0; i < m_Config.cats.size(); i++)
1.845 - {
1.846 - /* Find this category and map it in if we can */
1.847 - for (size_t j = 0; j < to_sort.size(); j++)
1.848 - {
1.849 - if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name),
1.850 - m_Categories[to_sort[j]]->obj->name) == 0)
1.851 - {
1.852 - /* Add to the real list and remove from the temporary */
1.853 - m_SortedCats.push_back(to_sort[j]);
1.854 - to_sort.erase(to_sort.iterAt(j));
1.855 - break;
1.856 - }
1.857 - }
1.858 - }
1.859 -
1.860 - /* Push any remaining items onto the unsorted list */
1.861 - for (size_t i = 0; i < to_sort.size(); i++)
1.862 - {
1.863 - m_UnsortedCats.push_back(to_sort[i]);
1.864 - }
1.865 -
1.866 - m_bCatsNeedResort = false;
1.867 -}
1.868 -
1.869 -void TopMenu::CreatePlayers(int max_clients)
1.870 -{
1.871 - m_max_clients = max_clients;
1.872 - m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1));
1.873 - memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1));
1.874 -}
1.875 -
1.876 -void TopMenu::TearDownClient(topmenu_player_t *player)
1.877 -{
1.878 - if (player->cats != NULL)
1.879 - {
1.880 - for (unsigned int i = 0; i < player->cat_count; i++)
1.881 - {
1.882 - topmenu_player_category_t *player_cat = &(player->cats[i]);
1.883 - if (player_cat->menu != NULL)
1.884 - {
1.885 - player_cat->menu->Destroy();
1.886 - }
1.887 - }
1.888 - delete [] player->cats;
1.889 - }
1.890 -
1.891 - if (player->root != NULL)
1.892 - {
1.893 - player->root->Destroy();
1.894 - }
1.895 -
1.896 - memset(player, 0, sizeof(topmenu_player_t));
1.897 -}
1.898 -
1.899 -bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
1.900 -{
1.901 - SMCError err;
1.902 - SMCStates states;
1.903 -
1.904 - if ((err = textparsers->ParseFile_SMC(file, this, &states))
1.905 - != SMCError_Okay)
1.906 - {
1.907 - const char *err_string = textparsers->GetSMCErrorString(err);
1.908 - if (!err_string)
1.909 - {
1.910 - err_string = "Unknown";
1.911 - }
1.912 -
1.913 - UTIL_Format(error, maxlength, "%s", err_string);
1.914 -
1.915 - return false;
1.916 - }
1.917 -
1.918 - return true;
1.919 -}
1.920 -
1.921 -bool TopMenu::OnIdentityRemoval(IdentityToken_t *owner)
1.922 -{
1.923 - /* First sweep the categories owned by us */
1.924 - CVector<unsigned int> obj_list;
1.925 - for (size_t i = 0; i < m_Categories.size(); i++)
1.926 - {
1.927 - if (m_Categories[i]->obj->owner == owner)
1.928 - {
1.929 - obj_list.push_back(m_Categories[i]->obj->object_id);
1.930 - }
1.931 - }
1.932 -
1.933 - for (size_t i = 0; i < obj_list.size(); i++)
1.934 - {
1.935 - RemoveFromMenu(obj_list[i]);
1.936 - }
1.937 -
1.938 - /* Now we can look for actual items */
1.939 - for (size_t i = 0; i < m_Objects.size(); i++)
1.940 - {
1.941 - if (m_Objects[i]->is_free)
1.942 - {
1.943 - continue;
1.944 - }
1.945 - if (m_Objects[i]->owner == owner)
1.946 - {
1.947 - assert(m_Objects[i]->type != TopMenuObject_Category);
1.948 - RemoveFromMenu(m_Objects[i]->object_id);
1.949 - }
1.950 - }
1.951 -
1.952 - return true;
1.953 -}
1.954 -
1.955 -#define PARSE_STATE_NONE 0
1.956 -#define PARSE_STATE_MAIN 1
1.957 -#define PARSE_STATE_CATEGORY 2
1.958 -unsigned int ignore_parse_level = 0;
1.959 -unsigned int current_parse_state = 0;
1.960 -config_category_t *cur_cat = NULL;
1.961 -
1.962 -void TopMenu::ReadSMC_ParseStart()
1.963 -{
1.964 - current_parse_state = PARSE_STATE_NONE;
1.965 - ignore_parse_level = 0;
1.966 - cur_cat = NULL;
1.967 -
1.968 - /* Reset the old config */
1.969 - m_Config.strings.Reset();
1.970 - for (size_t i = 0; i < m_Config.cats.size(); i++)
1.971 - {
1.972 - delete m_Config.cats[i];
1.973 - }
1.974 - m_Config.cats.clear();
1.975 -}
1.976 -
1.977 -SMCResult TopMenu::ReadSMC_NewSection(const SMCStates *states, const char *name)
1.978 -{
1.979 - if (ignore_parse_level)
1.980 - {
1.981 - ignore_parse_level++;
1.982 - }
1.983 - else
1.984 - {
1.985 - if (current_parse_state == PARSE_STATE_NONE)
1.986 - {
1.987 - if (strcmp(name, "Menu") == 0)
1.988 - {
1.989 - current_parse_state = PARSE_STATE_MAIN;
1.990 - }
1.991 - else
1.992 - {
1.993 - ignore_parse_level = 1;
1.994 - }
1.995 - }
1.996 - else if (current_parse_state == PARSE_STATE_MAIN)
1.997 - {
1.998 - cur_cat = new config_category_t;
1.999 - cur_cat->name = m_Config.strings.AddString(name);
1.1000 - m_Config.cats.push_back(cur_cat);
1.1001 - current_parse_state = PARSE_STATE_CATEGORY;
1.1002 - }
1.1003 - else
1.1004 - {
1.1005 - ignore_parse_level = 1;
1.1006 - }
1.1007 - }
1.1008 -
1.1009 - return SMCResult_Continue;
1.1010 -}
1.1011 -
1.1012 -SMCResult TopMenu::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.1013 -{
1.1014 - if (ignore_parse_level > 0
1.1015 - || current_parse_state != PARSE_STATE_CATEGORY
1.1016 - || cur_cat == NULL)
1.1017 - {
1.1018 - return SMCResult_Continue;
1.1019 - }
1.1020 -
1.1021 - if (strcmp(key, "item") == 0)
1.1022 - {
1.1023 - cur_cat->commands.push_back(m_Config.strings.AddString(value));
1.1024 - }
1.1025 -
1.1026 - return SMCResult_Continue;
1.1027 -}
1.1028 -
1.1029 -SMCResult TopMenu::ReadSMC_LeavingSection(const SMCStates *states)
1.1030 -{
1.1031 - if (ignore_parse_level)
1.1032 - {
1.1033 - ignore_parse_level--;
1.1034 - }
1.1035 - else
1.1036 - {
1.1037 - if (current_parse_state == PARSE_STATE_CATEGORY)
1.1038 - {
1.1039 - cur_cat = NULL;
1.1040 - current_parse_state = PARSE_STATE_MAIN;
1.1041 - }
1.1042 - else if (current_parse_state == PARSE_STATE_MAIN)
1.1043 - {
1.1044 - current_parse_state = PARSE_STATE_NONE;
1.1045 - }
1.1046 - }
1.1047 -
1.1048 - return SMCResult_Continue;
1.1049 -}
1.1050 -
1.1051 -unsigned int TopMenu::FindCategory(const char *name)
1.1052 -{
1.1053 - topmenu_object_t **p_obj = m_ObjLookup.retrieve(name);
1.1054 - if (!p_obj)
1.1055 - {
1.1056 - return 0;
1.1057 - }
1.1058 -
1.1059 - topmenu_object_t *obj = *p_obj;
1.1060 - if (obj->type != TopMenuObject_Category)
1.1061 - {
1.1062 - return 0;
1.1063 - }
1.1064 -
1.1065 - return obj->object_id;
1.1066 -}
1.1067 -
1.1068 -int _SortObjectNamesDescending(const void *ptr1, const void *ptr2)
1.1069 -{
1.1070 - obj_by_name_t *obj1 = (obj_by_name_t *)ptr1;
1.1071 - obj_by_name_t *obj2 = (obj_by_name_t *)ptr2;
1.1072 - return strcmp(obj1->name, obj2->name);
1.1073 -}
1.1074 -
1.1075 -unsigned int strncopy(char *dest, const char *src, size_t count)
1.1076 -{
1.1077 - if (!count)
1.1078 - {
1.1079 - return 0;
1.1080 - }
1.1081 -
1.1082 - char *start = dest;
1.1083 - while ((*src) && (--count))
1.1084 - {
1.1085 - *dest++ = *src++;
1.1086 - }
1.1087 - *dest = '\0';
1.1088 -
1.1089 - return (dest - start);
1.1090 -}
1.1091 -
1.1092 -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
1.1093 -{
1.1094 - va_list ap;
1.1095 - va_start(ap, fmt);
1.1096 - size_t len = vsnprintf(buffer, maxlength, fmt, ap);
1.1097 - va_end(ap);
1.1098 -
1.1099 - if (len >= maxlength)
1.1100 - {
1.1101 - buffer[maxlength - 1] = '\0';
1.1102 - return (maxlength - 1);
1.1103 - }
1.1104 - else
1.1105 - {
1.1106 - return len;
1.1107 - }
1.1108 -}
1.1109 +/**
1.1110 + * vim: set ts=4 :
1.1111 + * =============================================================================
1.1112 + * SourceMod Sample Extension
1.1113 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
1.1114 + * =============================================================================
1.1115 + *
1.1116 + * This program is free software; you can redistribute it and/or modify it under
1.1117 + * the terms of the GNU General Public License, version 3.0, as published by the
1.1118 + * Free Software Foundation.
1.1119 + *
1.1120 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.1121 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.1122 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.1123 + * details.
1.1124 + *
1.1125 + * You should have received a copy of the GNU General Public License along with
1.1126 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.1127 + *
1.1128 + * As a special exception, AlliedModders LLC gives you permission to link the
1.1129 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.1130 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.1131 + * by the Valve Corporation. You must obey the GNU General Public License in
1.1132 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.1133 + * this exception to all derivative works. AlliedModders LLC defines further
1.1134 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.1135 + * or <http://www.sourcemod.net/license.php>.
1.1136 + *
1.1137 + * Version: $Id$
1.1138 + */
1.1139 +
1.1140 +#include <stdlib.h>
1.1141 +#include <stdarg.h>
1.1142 +#include "TopMenu.h"
1.1143 +
1.1144 +struct obj_by_name_t
1.1145 +{
1.1146 + unsigned int obj_index;
1.1147 + char name[64];
1.1148 +};
1.1149 +
1.1150 +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2);
1.1151 +unsigned int strncopy(char *dest, const char *src, size_t count);
1.1152 +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
1.1153 +
1.1154 +TopMenu::TopMenu(ITopMenuObjectCallbacks *callbacks)
1.1155 +{
1.1156 + m_clients = NULL;
1.1157 + m_SerialNo = 1;
1.1158 + m_pTitle = callbacks;
1.1159 + m_max_clients = 0;
1.1160 +
1.1161 + if (playerhelpers->IsServerActivated())
1.1162 + {
1.1163 + CreatePlayers(playerhelpers->GetMaxClients());
1.1164 + }
1.1165 +}
1.1166 +
1.1167 +TopMenu::~TopMenu()
1.1168 +{
1.1169 + /* Delete all categories */
1.1170 + while (m_Categories.size())
1.1171 + {
1.1172 + RemoveFromMenu(m_Categories[0]->obj->object_id);
1.1173 + }
1.1174 +
1.1175 + /* Remove all objects */
1.1176 + for (size_t i = 0; i < m_Objects.size(); i++)
1.1177 + {
1.1178 + assert(m_Objects[i]->is_free == true);
1.1179 + delete m_Objects[i];
1.1180 + }
1.1181 +
1.1182 + m_pTitle->OnTopMenuObjectRemoved(this, 0);
1.1183 +
1.1184 + /* Delete all cached config entries */
1.1185 + for (size_t i = 0; i < m_Config.cats.size(); i++)
1.1186 + {
1.1187 + delete m_Config.cats[i];
1.1188 + }
1.1189 +
1.1190 + /* Sweep players */
1.1191 + for (size_t i = 0; i <= (size_t)m_max_clients; i++)
1.1192 + {
1.1193 + TearDownClient(&m_clients[i]);
1.1194 + }
1.1195 + delete [] m_clients;
1.1196 +}
1.1197 +
1.1198 +void TopMenu::OnClientConnected(int client)
1.1199 +{
1.1200 + if (m_clients == NULL)
1.1201 + {
1.1202 + return;
1.1203 + }
1.1204 +
1.1205 + topmenu_player_t *player = &m_clients[client];
1.1206 + TearDownClient(player);
1.1207 +}
1.1208 +
1.1209 +void TopMenu::OnClientDisconnected(int client)
1.1210 +{
1.1211 + if (m_clients == NULL)
1.1212 + {
1.1213 + return;
1.1214 + }
1.1215 +
1.1216 + topmenu_player_t *player = &m_clients[client];
1.1217 + TearDownClient(player);
1.1218 +}
1.1219 +
1.1220 +void TopMenu::OnServerActivated(int max_clients)
1.1221 +{
1.1222 + if (m_clients == NULL)
1.1223 + {
1.1224 + CreatePlayers(max_clients);
1.1225 + }
1.1226 +}
1.1227 +
1.1228 +unsigned int TopMenu::AddToMenu(const char *name,
1.1229 + TopMenuObjectType type,
1.1230 + ITopMenuObjectCallbacks *callbacks,
1.1231 + IdentityToken_t *owner,
1.1232 + const char *cmdname,
1.1233 + FlagBits flags,
1.1234 + unsigned int parent)
1.1235 +{
1.1236 + return AddToMenu2(name, type, callbacks, owner, cmdname, flags, parent, NULL);
1.1237 +}
1.1238 +
1.1239 +unsigned int TopMenu::AddToMenu2(const char *name,
1.1240 + TopMenuObjectType type,
1.1241 + ITopMenuObjectCallbacks *callbacks,
1.1242 + IdentityToken_t *owner,
1.1243 + const char *cmdname,
1.1244 + FlagBits flags,
1.1245 + unsigned int parent,
1.1246 + const char *info_string)
1.1247 +{
1.1248 + /* Sanity checks */
1.1249 + if (type == TopMenuObject_Category && parent != 0)
1.1250 + {
1.1251 + return 0;
1.1252 + }
1.1253 + else if (type == TopMenuObject_Item && parent == 0)
1.1254 + {
1.1255 + return 0;
1.1256 + }
1.1257 + else if (m_ObjLookup.retrieve(name) != NULL)
1.1258 + {
1.1259 + return 0;
1.1260 + }
1.1261 + else if (type != TopMenuObject_Item && type != TopMenuObject_Category)
1.1262 + {
1.1263 + return 0;
1.1264 + }
1.1265 +
1.1266 + /* If we're adding an item, make sure the parent is valid,
1.1267 + * and that the parent is a category.
1.1268 + */
1.1269 + topmenu_object_t *parent_obj = NULL;
1.1270 + topmenu_category_t *parent_cat = NULL;
1.1271 + if (type == TopMenuObject_Item)
1.1272 + {
1.1273 + /* Check parent index. Note it will be >= 1 here. */
1.1274 + if (parent > m_Objects.size() || m_Objects[parent - 1]->is_free)
1.1275 + {
1.1276 + return 0;
1.1277 + }
1.1278 + parent_obj = m_Objects[parent - 1];
1.1279 +
1.1280 + /* Find an equivalent pointer in the category array. */
1.1281 + for (size_t i = 0; i < m_Categories.size(); i++)
1.1282 + {
1.1283 + if (m_Categories[i]->obj == parent_obj)
1.1284 + {
1.1285 + parent_cat = m_Categories[i];
1.1286 + break;
1.1287 + }
1.1288 + }
1.1289 +
1.1290 + /* If none was found, leave. */
1.1291 + if (parent_cat == NULL)
1.1292 + {
1.1293 + return 0;
1.1294 + }
1.1295 + }
1.1296 +
1.1297 + /* Re-use an old object pointer if we can. */
1.1298 + topmenu_object_t *obj = NULL;
1.1299 + for (size_t i = 0; i < m_Objects.size(); i++)
1.1300 + {
1.1301 + if (m_Objects[i]->is_free == true)
1.1302 + {
1.1303 + obj = m_Objects[i];
1.1304 + break;
1.1305 + }
1.1306 + }
1.1307 +
1.1308 + /* Otherwise, allocate a new one. */
1.1309 + if (obj == NULL)
1.1310 + {
1.1311 + obj = new topmenu_object_t;
1.1312 + obj->object_id = ((unsigned int)m_Objects.size()) + 1;
1.1313 + m_Objects.push_back(obj);
1.1314 + }
1.1315 +
1.1316 + /* Initialize the object's properties. */
1.1317 + obj->callbacks = callbacks;
1.1318 + obj->flags = flags;
1.1319 + obj->owner = owner;
1.1320 + obj->type = type;
1.1321 + obj->is_free = false;
1.1322 + obj->parent = parent_obj;
1.1323 + strncopy(obj->name, name, sizeof(obj->name));
1.1324 + strncopy(obj->cmdname, cmdname ? cmdname : "", sizeof(obj->cmdname));
1.1325 + strncopy(obj->info, info_string ? info_string : "", sizeof(obj->info));
1.1326 +
1.1327 + if (obj->type == TopMenuObject_Category)
1.1328 + {
1.1329 + /* Create a new category entry */
1.1330 + topmenu_category_t *cat = new topmenu_category_t;
1.1331 + cat->obj = obj;
1.1332 + cat->reorder = false;
1.1333 + cat->serial = 1;
1.1334 +
1.1335 + /* Add it, then update our serial change number. */
1.1336 + m_Categories.push_back(cat);
1.1337 + m_SerialNo++;
1.1338 +
1.1339 + /* Updating sorting info */
1.1340 + m_bCatsNeedResort = true;
1.1341 + }
1.1342 + else if (obj->type == TopMenuObject_Item)
1.1343 + {
1.1344 + /* Update the category, mark it as needing changes */
1.1345 + parent_cat->obj_list.push_back(obj);
1.1346 + parent_cat->reorder = true;
1.1347 + parent_cat->serial++;
1.1348 +
1.1349 + /* If the category just went from 0 to 1 items, mark it as
1.1350 + * changed, so clients get the category drawn.
1.1351 + */
1.1352 + if (parent_cat->obj_list.size() == 1)
1.1353 + {
1.1354 + m_SerialNo++;
1.1355 + }
1.1356 + }
1.1357 +
1.1358 + m_ObjLookup.insert(name, obj);
1.1359 +
1.1360 + return obj->object_id;
1.1361 +}
1.1362 +
1.1363 +const char *TopMenu::GetObjectInfoString(unsigned int object_id)
1.1364 +{
1.1365 + if (object_id == 0
1.1366 + || object_id > m_Objects.size()
1.1367 + || m_Objects[object_id - 1]->is_free)
1.1368 + {
1.1369 + return NULL;
1.1370 + }
1.1371 +
1.1372 + topmenu_object_t *obj = m_Objects[object_id - 1];
1.1373 +
1.1374 + return obj->info;
1.1375 +}
1.1376 +
1.1377 +const char *TopMenu::GetObjectName(unsigned int object_id)
1.1378 +{
1.1379 + if (object_id == 0
1.1380 + || object_id > m_Objects.size()
1.1381 + || m_Objects[object_id - 1]->is_free)
1.1382 + {
1.1383 + return NULL;
1.1384 + }
1.1385 +
1.1386 + topmenu_object_t *obj = m_Objects[object_id - 1];
1.1387 +
1.1388 + return obj->name;
1.1389 +}
1.1390 +
1.1391 +void TopMenu::RemoveFromMenu(unsigned int object_id)
1.1392 +{
1.1393 + if (object_id == 0
1.1394 + || object_id > m_Objects.size()
1.1395 + || m_Objects[object_id - 1]->is_free)
1.1396 + {
1.1397 + return;
1.1398 + }
1.1399 +
1.1400 + topmenu_object_t *obj = m_Objects[object_id - 1];
1.1401 +
1.1402 + m_ObjLookup.remove(obj->name);
1.1403 +
1.1404 + if (obj->type == TopMenuObject_Category)
1.1405 + {
1.1406 + /* Find it in the category list. */
1.1407 + for (size_t i = 0; i < m_Categories.size(); i++)
1.1408 + {
1.1409 + if (m_Categories[i]->obj == obj)
1.1410 + {
1.1411 + /* Mark all children as removed + free. Note we could
1.1412 + * call into RemoveMenuItem() for this, but it'd be very
1.1413 + * inefficient!
1.1414 + */
1.1415 + topmenu_category_t *cat = m_Categories[i];
1.1416 + for (size_t j = 0; j < m_Categories[i]->obj_list.size(); j++)
1.1417 + {
1.1418 + cat->obj_list[j]->callbacks->OnTopMenuObjectRemoved(this, cat->obj_list[j]->object_id);
1.1419 + cat->obj_list[j]->is_free = true;
1.1420 + }
1.1421 +
1.1422 + /* Remove the category from the list, then delete it. */
1.1423 + m_Categories.erase(m_Categories.iterAt(i));
1.1424 + delete cat;
1.1425 + break;
1.1426 + }
1.1427 + }
1.1428 +
1.1429 + /* Update the root as changed. */
1.1430 + m_SerialNo++;
1.1431 + m_bCatsNeedResort = true;
1.1432 + }
1.1433 + else if (obj->type == TopMenuObject_Item)
1.1434 + {
1.1435 + /* Find the category this item is in. */
1.1436 + topmenu_category_t *parent_cat = NULL;
1.1437 + for (size_t i = 0; i < m_Categories.size(); i++)
1.1438 + {
1.1439 + if (m_Categories[i]->obj == obj->parent)
1.1440 + {
1.1441 + parent_cat = m_Categories[i];
1.1442 + break;
1.1443 + }
1.1444 + }
1.1445 +
1.1446 + /* Erase it from the category's lists. */
1.1447 + if (parent_cat)
1.1448 + {
1.1449 + for (size_t i = 0; i < parent_cat->obj_list.size(); i++)
1.1450 + {
1.1451 + if (parent_cat->obj_list[i] == obj)
1.1452 + {
1.1453 + parent_cat->obj_list.erase(parent_cat->obj_list.iterAt(i));
1.1454 +
1.1455 + /* If this category now has no items, mark root as changed
1.1456 + * so clients won't get the category drawn anymore.
1.1457 + */
1.1458 + if (parent_cat->obj_list.size() == 0)
1.1459 + {
1.1460 + m_SerialNo++;
1.1461 + }
1.1462 + break;
1.1463 + }
1.1464 + }
1.1465 +
1.1466 + /* Update the category as changed. */
1.1467 + parent_cat->reorder = true;
1.1468 + parent_cat->serial++;
1.1469 + }
1.1470 + }
1.1471 +
1.1472 + /* The callbacks pointer is still valid, so fire away! */
1.1473 + obj->callbacks->OnTopMenuObjectRemoved(this, object_id);
1.1474 +
1.1475 + /* Finally, mark the object as free. */
1.1476 + obj->is_free = true;
1.1477 +}
1.1478 +
1.1479 +bool TopMenu::DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position)
1.1480 +{
1.1481 + if (m_clients == NULL)
1.1482 + {
1.1483 + return false;
1.1484 + }
1.1485 +
1.1486 + IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
1.1487 + if (!pPlayer->IsInGame())
1.1488 + {
1.1489 + return false;
1.1490 + }
1.1491 +
1.1492 + UpdateClientRoot(client, pPlayer);
1.1493 +
1.1494 + topmenu_player_t *pClient = &m_clients[client];
1.1495 + if (pClient->root == NULL)
1.1496 + {
1.1497 + return false;
1.1498 + }
1.1499 +
1.1500 + bool return_value = false;
1.1501 +
1.1502 + if (position == TopMenuPosition_LastCategory &&
1.1503 + pClient->last_category < m_Categories.size())
1.1504 + {
1.1505 + return_value = DisplayCategory(client, pClient->last_category, hold_time, true);
1.1506 + if (!return_value)
1.1507 + {
1.1508 + return_value = pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
1.1509 + }
1.1510 + }
1.1511 + else if (position == TopMenuPosition_LastRoot)
1.1512 + {
1.1513 + pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
1.1514 + }
1.1515 + else if (position == TopMenuPosition_Start)
1.1516 + {
1.1517 + pClient->last_position = 0;
1.1518 + pClient->last_category = 0;
1.1519 + return_value = pClient->root->Display(client, hold_time);
1.1520 + }
1.1521 +
1.1522 + return return_value;
1.1523 +}
1.1524 +
1.1525 +bool TopMenu::DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position)
1.1526 +{
1.1527 + UpdateClientCategory(client, category);
1.1528 +
1.1529 + topmenu_player_t *pClient = &m_clients[client];
1.1530 + if (category >= pClient->cat_count
1.1531 + || pClient->cats[category].menu == NULL)
1.1532 + {
1.1533 + return false;
1.1534 + }
1.1535 +
1.1536 + bool return_value = false;
1.1537 +
1.1538 + topmenu_player_category_t *player_cat = &(pClient->cats[category]);
1.1539 +
1.1540 + pClient->last_category = category;
1.1541 + if (last_position)
1.1542 + {
1.1543 + return_value = player_cat->menu->DisplayAtItem(client, hold_time, pClient->last_position);
1.1544 + }
1.1545 + else
1.1546 + {
1.1547 + return_value = player_cat->menu->Display(client, hold_time);
1.1548 + }
1.1549 +
1.1550 + return return_value;
1.1551 +}
1.1552 +
1.1553 +void TopMenu::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
1.1554 +{
1.1555 + const char *item_name = menu->GetItemInfo(item, NULL);
1.1556 + if (!item_name)
1.1557 + {
1.1558 + return;
1.1559 + }
1.1560 +
1.1561 + topmenu_object_t *obj;
1.1562 + topmenu_player_t *pClient = &m_clients[client];
1.1563 + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.1564 + if (pObject == NULL)
1.1565 + {
1.1566 + return;
1.1567 + }
1.1568 +
1.1569 + obj = *pObject;
1.1570 +
1.1571 + /* We now have the object... what do we do with it? */
1.1572 + if (obj->type == TopMenuObject_Category)
1.1573 + {
1.1574 + /* If it's a category, the user wants to view it.. */
1.1575 + for (size_t i = 0; i < m_Categories.size(); i++)
1.1576 + {
1.1577 + if (m_Categories[i]->obj == obj)
1.1578 + {
1.1579 + pClient->last_root_pos = item_on_page;
1.1580 + if (!DisplayCategory(client, (unsigned int)i, MENU_TIME_FOREVER, false))
1.1581 + {
1.1582 + /* If we can't display the category, re-display the root menu */
1.1583 + DisplayMenu(client, MENU_TIME_FOREVER, TopMenuPosition_LastRoot);
1.1584 + }
1.1585 + break;
1.1586 + }
1.1587 + }
1.1588 + }
1.1589 + else
1.1590 + {
1.1591 + pClient->last_position = item_on_page;
1.1592 +
1.1593 + /* Re-check access in case this user had their credentials revoked */
1.1594 + if (obj->cmdname[0] != '\0' && !adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
1.1595 + {
1.1596 + DisplayMenu(client, 0, TopMenuPosition_LastCategory);
1.1597 + return;
1.1598 + }
1.1599 +
1.1600 + /* Pass the information on to the callback */
1.1601 + obj->callbacks->OnTopMenuSelectOption(this, client, obj->object_id);
1.1602 + }
1.1603 +}
1.1604 +
1.1605 +void TopMenu::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
1.1606 +{
1.1607 + const char *item_name = menu->GetItemInfo(item, NULL);
1.1608 + if (!item_name)
1.1609 + {
1.1610 + return;
1.1611 + }
1.1612 +
1.1613 + topmenu_object_t *obj;
1.1614 + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.1615 + if (pObject == NULL)
1.1616 + {
1.1617 + return;
1.1618 + }
1.1619 +
1.1620 + obj = *pObject;
1.1621 +
1.1622 + style = obj->callbacks->OnTopMenuDrawOption(this, client, obj->object_id);
1.1623 + if (style != ITEMDRAW_DEFAULT)
1.1624 + {
1.1625 + return;
1.1626 + }
1.1627 +
1.1628 + if (obj->cmdname[0] == '\0')
1.1629 + {
1.1630 + return;
1.1631 + }
1.1632 +
1.1633 + if (!adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
1.1634 + {
1.1635 + style = ITEMDRAW_IGNORE;
1.1636 + }
1.1637 +}
1.1638 +
1.1639 +unsigned int TopMenu::OnMenuDisplayItem(IBaseMenu *menu,
1.1640 + int client,
1.1641 + IMenuPanel *panel,
1.1642 + unsigned int item,
1.1643 + const ItemDrawInfo &dr)
1.1644 +{
1.1645 + const char *item_name = menu->GetItemInfo(item, NULL);
1.1646 + if (!item_name)
1.1647 + {
1.1648 + return 0;
1.1649 + }
1.1650 +
1.1651 + topmenu_object_t *obj;
1.1652 + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
1.1653 + if (pObject == NULL)
1.1654 + {
1.1655 + return 0;
1.1656 + }
1.1657 +
1.1658 + obj = *pObject;
1.1659 +
1.1660 + /* Ask the object to render the text for this client */
1.1661 + char renderbuf[64];
1.1662 + obj->callbacks->OnTopMenuDisplayOption(this, client, obj->object_id, renderbuf, sizeof(renderbuf));
1.1663 +
1.1664 + /* Build the new draw info */
1.1665 + ItemDrawInfo new_dr = dr;
1.1666 + new_dr.display = renderbuf;
1.1667 +
1.1668 + /* Man I love the menu API. Ask the panel to draw the item and give the position
1.1669 + * back to Core's renderer. This way we don't have to worry about keeping the
1.1670 + * render buffer static!
1.1671 + */
1.1672 + return panel->DrawItem(new_dr);
1.1673 +}
1.1674 +
1.1675 +void TopMenu::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
1.1676 +{
1.1677 + if (reason == MenuCancel_ExitBack)
1.1678 + {
1.1679 + /* If the client chose exit back, they were on a category menu, and we can
1.1680 + * now display the root menu from the last known position.
1.1681 + */
1.1682 + DisplayMenu(client, 0, TopMenuPosition_LastRoot);
1.1683 + }
1.1684 +}
1.1685 +
1.1686 +void TopMenu::UpdateClientRoot(int client, IGamePlayer *pGamePlayer)
1.1687 +{
1.1688 + topmenu_player_t *pClient = &m_clients[client];
1.1689 + IGamePlayer *pPlayer = pGamePlayer ? pGamePlayer : playerhelpers->GetGamePlayer(client);
1.1690 +
1.1691 + /* Determine if an update is necessary */
1.1692 + bool is_update_needed = false;
1.1693 + if (pClient->menu_serial != m_SerialNo)
1.1694 + {
1.1695 + is_update_needed = true;
1.1696 + }
1.1697 + else if (pPlayer->GetUserId() != pClient->user_id)
1.1698 + {
1.1699 + is_update_needed = true;
1.1700 + }
1.1701 +
1.1702 + /* If no update is needed at the root level, just leave now */
1.1703 + if (!is_update_needed)
1.1704 + {
1.1705 + return;
1.1706 + }
1.1707 +
1.1708 + /* First we need to flush the cache... */
1.1709 + TearDownClient(pClient);
1.1710 +
1.1711 + /* Now, rebuild the category list, but don't create menus */
1.1712 + if (m_Categories.size() == 0)
1.1713 + {
1.1714 + pClient->cat_count = 0;
1.1715 + pClient->cats = NULL;
1.1716 + }
1.1717 + else
1.1718 + {
1.1719 + pClient->cat_count = (unsigned int)m_Categories.size();
1.1720 + pClient->cats = new topmenu_player_category_t[pClient->cat_count];
1.1721 + memset(pClient->cats, 0, sizeof(topmenu_player_category_t) * pClient->cat_count);
1.1722 + }
1.1723 +
1.1724 + /* Re-sort the root categories if needed */
1.1725 + SortCategoriesIfNeeded();
1.1726 +
1.1727 + /* Build the root menu */
1.1728 + IBaseMenu *root_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
1.1729 +
1.1730 + /* Add the sorted items */
1.1731 + for (size_t i = 0; i < m_SortedCats.size(); i++)
1.1732 + {
1.1733 + if (m_Categories[m_SortedCats[i]]->obj_list.size() == 0)
1.1734 + {
1.1735 + continue;
1.1736 + }
1.1737 + root_menu->AppendItem(m_Categories[m_SortedCats[i]]->obj->name, ItemDrawInfo(""));
1.1738 + }
1.1739 +
1.1740 + /* Now we need to handle un-sorted items. This is slightly trickier, as we need to
1.1741 + * pre-render each category name, and cache those names. Phew!
1.1742 + */
1.1743 + if (m_UnsortedCats.size())
1.1744 + {
1.1745 + obj_by_name_t *item_list = new obj_by_name_t[m_UnsortedCats.size()];
1.1746 + for (size_t i = 0; i < m_UnsortedCats.size(); i++)
1.1747 + {
1.1748 + obj_by_name_t *temp_obj = &item_list[i];
1.1749 + topmenu_object_t *obj = m_Categories[m_UnsortedCats[i]]->obj;
1.1750 + obj->callbacks->OnTopMenuDisplayOption(this,
1.1751 + client,
1.1752 + obj->object_id,
1.1753 + temp_obj->name,
1.1754 + sizeof(temp_obj->name));
1.1755 + temp_obj->obj_index = m_UnsortedCats[i];
1.1756 + }
1.1757 +
1.1758 + /* Sort our temp list */
1.1759 + qsort(item_list, m_UnsortedCats.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
1.1760 +
1.1761 + /* Add the new sorted categories */
1.1762 + for (size_t i = 0; i < m_UnsortedCats.size(); i++)
1.1763 + {
1.1764 + if (m_Categories[item_list[i].obj_index]->obj_list.size() == 0)
1.1765 + {
1.1766 + continue;
1.1767 + }
1.1768 + root_menu->AppendItem(m_Categories[item_list[i].obj_index]->obj->name, ItemDrawInfo(""));
1.1769 + }
1.1770 +
1.1771 + delete [] item_list;
1.1772 + }
1.1773 +
1.1774 + /* Set the menu's title */
1.1775 + char renderbuf[128];
1.1776 + m_pTitle->OnTopMenuDisplayTitle(this, client, 0, renderbuf, sizeof(renderbuf));
1.1777 + root_menu->SetDefaultTitle(renderbuf);
1.1778 +
1.1779 + /* The client is now fully updated */
1.1780 + pClient->root = root_menu;
1.1781 + pClient->user_id = pPlayer->GetUserId();
1.1782 + pClient->menu_serial = m_SerialNo;
1.1783 + pClient->last_position = 0;
1.1784 + pClient->last_category = 0;
1.1785 + pClient->last_root_pos = 0;
1.1786 +}
1.1787 +
1.1788 +void TopMenu::UpdateClientCategory(int client, unsigned int category)
1.1789 +{
1.1790 + /* Update the client's root menu just in case it needs it. This
1.1791 + * will validate that we have both a valid client and a valid
1.1792 + * category structure for that client.
1.1793 + */
1.1794 + UpdateClientRoot(client);
1.1795 +
1.1796 + /* Now it's guaranteed that our category tables will be usable */
1.1797 + topmenu_player_t *pClient = &m_clients[client];
1.1798 + topmenu_category_t *cat = m_Categories[category];
1.1799 + topmenu_player_category_t *player_cat = &(pClient->cats[category]);
1.1800 +
1.1801 + /* Does the category actually need updating? */
1.1802 + if (player_cat->serial == cat->serial)
1.1803 + {
1.1804 + return;
1.1805 + }
1.1806 +
1.1807 + /* Destroy any existing menu */
1.1808 + if (player_cat->menu)
1.1809 + {
1.1810 + player_cat->menu->Destroy();
1.1811 + player_cat->menu = NULL;
1.1812 + }
1.1813 +
1.1814 + if (pClient->last_category == category)
1.1815 + {
1.1816 + pClient->last_position = 0;
1.1817 + }
1.1818 +
1.1819 + IBaseMenu *cat_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
1.1820 +
1.1821 + /* Categories get an "exit back" button */
1.1822 + cat_menu->SetMenuOptionFlags(cat_menu->GetMenuOptionFlags() | MENUFLAG_BUTTON_EXITBACK);
1.1823 +
1.1824 + /* Re-sort the category if needed */
1.1825 + SortCategoryIfNeeded(category);
1.1826 +
1.1827 + /* Build the menu with the sorted items first */
1.1828 + for (size_t i = 0; i < cat->sorted.size(); i++)
1.1829 + {
1.1830 + cat_menu->AppendItem(cat->sorted[i]->name, ItemDrawInfo(""));
1.1831 + }
1.1832 +
1.1833 + /* Now handle unsorted items */
1.1834 + if (cat->unsorted.size())
1.1835 + {
1.1836 + /* Build a list of the item names */
1.1837 + obj_by_name_t *item_list = new obj_by_name_t[cat->unsorted.size()];
1.1838 + for (size_t i = 0; i < cat->unsorted.size(); i++)
1.1839 + {
1.1840 + obj_by_name_t *item = &item_list[i];
1.1841 + topmenu_object_t *obj = cat->unsorted[i];
1.1842 + obj->callbacks->OnTopMenuDisplayOption(this,
1.1843 + client,
1.1844 + obj->object_id,
1.1845 + item->name,
1.1846 + sizeof(item->name));
1.1847 + item->obj_index = (unsigned int)i;
1.1848 + }
1.1849 +
1.1850 + /* Sort the names */
1.1851 + qsort(item_list, cat->unsorted.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
1.1852 +
1.1853 + /* Add to the menu */
1.1854 + for (size_t i = 0; i < cat->unsorted.size(); i++)
1.1855 + {
1.1856 + cat_menu->AppendItem(cat->unsorted[item_list[i].obj_index]->name, ItemDrawInfo(""));
1.1857 + }
1.1858 +
1.1859 + delete [] item_list;
1.1860 + }
1.1861 +
1.1862 + /* Set the menu's title */
1.1863 + char renderbuf[128];
1.1864 + cat->obj->callbacks->OnTopMenuDisplayTitle(this,
1.1865 + client,
1.1866 + cat->obj->object_id,
1.1867 + renderbuf,
1.1868 + sizeof(renderbuf));
1.1869 + cat_menu->SetDefaultTitle(renderbuf);
1.1870 +
1.1871 + /* We're done! */
1.1872 + player_cat->menu = cat_menu;
1.1873 + player_cat->serial = cat->serial;
1.1874 +}
1.1875 +
1.1876 +void TopMenu::SortCategoryIfNeeded(unsigned int category)
1.1877 +{
1.1878 + topmenu_category_t *cat = m_Categories[category];
1.1879 + if (!cat->reorder)
1.1880 + {
1.1881 + return;
1.1882 + }
1.1883 +
1.1884 + cat->sorted.clear();
1.1885 + cat->unsorted.clear();
1.1886 +
1.1887 + if (cat->obj_list.size() == 0)
1.1888 + {
1.1889 + cat->reorder = false;
1.1890 + return;
1.1891 + }
1.1892 +
1.1893 + CVector<unsigned int> to_sort;
1.1894 + for (size_t i = 0; i < cat->obj_list.size(); i++)
1.1895 + {
1.1896 + to_sort.push_back(i);
1.1897 + }
1.1898 +
1.1899 + /* Find a matching category in the configs */
1.1900 + config_category_t *config_cat = NULL;
1.1901 + for (size_t i = 0; i < m_Config.cats.size(); i++)
1.1902 + {
1.1903 + if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), cat->obj->name) == 0)
1.1904 + {
1.1905 + config_cat = m_Config.cats[i];
1.1906 + break;
1.1907 + }
1.1908 + }
1.1909 +
1.1910 + /* If there is a matching category, build a pre-sorted item list */
1.1911 + if (config_cat != NULL)
1.1912 + {
1.1913 + /* Find matching objects in this category */
1.1914 + for (size_t i = 0; i < config_cat->commands.size(); i++)
1.1915 + {
1.1916 + const char *config_name = m_Config.strings.GetString(config_cat->commands[i]);
1.1917 + for (size_t j = 0; j < to_sort.size(); j++)
1.1918 + {
1.1919 + if (strcmp(config_name, cat->obj_list[to_sort[j]]->name) == 0)
1.1920 + {
1.1921 + /* Place in the final list, then remove from the temporary list */
1.1922 + cat->sorted.push_back(cat->obj_list[to_sort[j]]);
1.1923 + to_sort.erase(to_sort.iterAt(j));
1.1924 + break;
1.1925 + }
1.1926 + }
1.1927 + }
1.1928 + }
1.1929 +
1.1930 + /* Push any remaining items onto the unsorted list */
1.1931 + for (size_t i = 0; i < to_sort.size(); i++)
1.1932 + {
1.1933 + cat->unsorted.push_back(cat->obj_list[to_sort[i]]);
1.1934 + }
1.1935 +
1.1936 + cat->reorder = false;
1.1937 +}
1.1938 +
1.1939 +void TopMenu::SortCategoriesIfNeeded()
1.1940 +{
1.1941 + if (!m_bCatsNeedResort)
1.1942 + {
1.1943 + return;
1.1944 + }
1.1945 +
1.1946 + /* Clear sort results */
1.1947 + m_SortedCats.clear();
1.1948 + m_UnsortedCats.clear();
1.1949 +
1.1950 + if (m_Categories.size() == 0)
1.1951 + {
1.1952 + m_bCatsNeedResort = false;
1.1953 + return;
1.1954 + }
1.1955 +
1.1956 + CVector<unsigned int> to_sort;
1.1957 + for (unsigned int i = 0; i < (unsigned int)m_Categories.size(); i++)
1.1958 + {
1.1959 + to_sort.push_back(i);
1.1960 + }
1.1961 +
1.1962 + /* If we have any predefined categories, add them in as they appear. */
1.1963 + for (size_t i= 0; i < m_Config.cats.size(); i++)
1.1964 + {
1.1965 + /* Find this category and map it in if we can */
1.1966 + for (size_t j = 0; j < to_sort.size(); j++)
1.1967 + {
1.1968 + if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name),
1.1969 + m_Categories[to_sort[j]]->obj->name) == 0)
1.1970 + {
1.1971 + /* Add to the real list and remove from the temporary */
1.1972 + m_SortedCats.push_back(to_sort[j]);
1.1973 + to_sort.erase(to_sort.iterAt(j));
1.1974 + break;
1.1975 + }
1.1976 + }
1.1977 + }
1.1978 +
1.1979 + /* Push any remaining items onto the unsorted list */
1.1980 + for (size_t i = 0; i < to_sort.size(); i++)
1.1981 + {
1.1982 + m_UnsortedCats.push_back(to_sort[i]);
1.1983 + }
1.1984 +
1.1985 + m_bCatsNeedResort = false;
1.1986 +}
1.1987 +
1.1988 +void TopMenu::CreatePlayers(int max_clients)
1.1989 +{
1.1990 + m_max_clients = max_clients;
1.1991 + m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1));
1.1992 + memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1));
1.1993 +}
1.1994 +
1.1995 +void TopMenu::TearDownClient(topmenu_player_t *player)
1.1996 +{
1.1997 + if (player->cats != NULL)
1.1998 + {
1.1999 + for (unsigned int i = 0; i < player->cat_count; i++)
1.2000 + {
1.2001 + topmenu_player_category_t *player_cat = &(player->cats[i]);
1.2002 + if (player_cat->menu != NULL)
1.2003 + {
1.2004 + player_cat->menu->Destroy();
1.2005 + }
1.2006 + }
1.2007 + delete [] player->cats;
1.2008 + }
1.2009 +
1.2010 + if (player->root != NULL)
1.2011 + {
1.2012 + player->root->Destroy();
1.2013 + }
1.2014 +
1.2015 + memset(player, 0, sizeof(topmenu_player_t));
1.2016 +}
1.2017 +
1.2018 +bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
1.2019 +{
1.2020 + SMCError err;
1.2021 + SMCStates states;
1.2022 +
1.2023 + if ((err = textparsers->ParseFile_SMC(file, this, &states))
1.2024 + != SMCError_Okay)
1.2025 + {
1.2026 + const char *err_string = textparsers->GetSMCErrorString(err);
1.2027 + if (!err_string)
1.2028 + {
1.2029 + err_string = "Unknown";
1.2030 + }
1.2031 +
1.2032 + UTIL_Format(error, maxlength, "%s", err_string);
1.2033 +
1.2034 + return false;
1.2035 + }
1.2036 +
1.2037 + return true;
1.2038 +}
1.2039 +
1.2040 +bool TopMenu::OnIdentityRemoval(IdentityToken_t *owner)
1.2041 +{
1.2042 + /* First sweep the categories owned by us */
1.2043 + CVector<unsigned int> obj_list;
1.2044 + for (size_t i = 0; i < m_Categories.size(); i++)
1.2045 + {
1.2046 + if (m_Categories[i]->obj->owner == owner)
1.2047 + {
1.2048 + obj_list.push_back(m_Categories[i]->obj->object_id);
1.2049 + }
1.2050 + }
1.2051 +
1.2052 + for (size_t i = 0; i < obj_list.size(); i++)
1.2053 + {
1.2054 + RemoveFromMenu(obj_list[i]);
1.2055 + }
1.2056 +
1.2057 + /* Now we can look for actual items */
1.2058 + for (size_t i = 0; i < m_Objects.size(); i++)
1.2059 + {
1.2060 + if (m_Objects[i]->is_free)
1.2061 + {
1.2062 + continue;
1.2063 + }
1.2064 + if (m_Objects[i]->owner == owner)
1.2065 + {
1.2066 + assert(m_Objects[i]->type != TopMenuObject_Category);
1.2067 + RemoveFromMenu(m_Objects[i]->object_id);
1.2068 + }
1.2069 + }
1.2070 +
1.2071 + return true;
1.2072 +}
1.2073 +
1.2074 +#define PARSE_STATE_NONE 0
1.2075 +#define PARSE_STATE_MAIN 1
1.2076 +#define PARSE_STATE_CATEGORY 2
1.2077 +unsigned int ignore_parse_level = 0;
1.2078 +unsigned int current_parse_state = 0;
1.2079 +config_category_t *cur_cat = NULL;
1.2080 +
1.2081 +void TopMenu::ReadSMC_ParseStart()
1.2082 +{
1.2083 + current_parse_state = PARSE_STATE_NONE;
1.2084 + ignore_parse_level = 0;
1.2085 + cur_cat = NULL;
1.2086 +
1.2087 + /* Reset the old config */
1.2088 + m_Config.strings.Reset();
1.2089 + for (size_t i = 0; i < m_Config.cats.size(); i++)
1.2090 + {
1.2091 + delete m_Config.cats[i];
1.2092 + }
1.2093 + m_Config.cats.clear();
1.2094 +}
1.2095 +
1.2096 +SMCResult TopMenu::ReadSMC_NewSection(const SMCStates *states, const char *name)
1.2097 +{
1.2098 + if (ignore_parse_level)
1.2099 + {
1.2100 + ignore_parse_level++;
1.2101 + }
1.2102 + else
1.2103 + {
1.2104 + if (current_parse_state == PARSE_STATE_NONE)
1.2105 + {
1.2106 + if (strcmp(name, "Menu") == 0)
1.2107 + {
1.2108 + current_parse_state = PARSE_STATE_MAIN;
1.2109 + }
1.2110 + else
1.2111 + {
1.2112 + ignore_parse_level = 1;
1.2113 + }
1.2114 + }
1.2115 + else if (current_parse_state == PARSE_STATE_MAIN)
1.2116 + {
1.2117 + cur_cat = new config_category_t;
1.2118 + cur_cat->name = m_Config.strings.AddString(name);
1.2119 + m_Config.cats.push_back(cur_cat);
1.2120 + current_parse_state = PARSE_STATE_CATEGORY;
1.2121 + }
1.2122 + else
1.2123 + {
1.2124 + ignore_parse_level = 1;
1.2125 + }
1.2126 + }
1.2127 +
1.2128 + return SMCResult_Continue;
1.2129 +}
1.2130 +
1.2131 +SMCResult TopMenu::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.2132 +{
1.2133 + if (ignore_parse_level > 0
1.2134 + || current_parse_state != PARSE_STATE_CATEGORY
1.2135 + || cur_cat == NULL)
1.2136 + {
1.2137 + return SMCResult_Continue;
1.2138 + }
1.2139 +
1.2140 + if (strcmp(key, "item") == 0)
1.2141 + {
1.2142 + cur_cat->commands.push_back(m_Config.strings.AddString(value));
1.2143 + }
1.2144 +
1.2145 + return SMCResult_Continue;
1.2146 +}
1.2147 +
1.2148 +SMCResult TopMenu::ReadSMC_LeavingSection(const SMCStates *states)
1.2149 +{
1.2150 + if (ignore_parse_level)
1.2151 + {
1.2152 + ignore_parse_level--;
1.2153 + }
1.2154 + else
1.2155 + {
1.2156 + if (current_parse_state == PARSE_STATE_CATEGORY)
1.2157 + {
1.2158 + cur_cat = NULL;
1.2159 + current_parse_state = PARSE_STATE_MAIN;
1.2160 + }
1.2161 + else if (current_parse_state == PARSE_STATE_MAIN)
1.2162 + {
1.2163 + current_parse_state = PARSE_STATE_NONE;
1.2164 + }
1.2165 + }
1.2166 +
1.2167 + return SMCResult_Continue;
1.2168 +}
1.2169 +
1.2170 +unsigned int TopMenu::FindCategory(const char *name)
1.2171 +{
1.2172 + topmenu_object_t **p_obj = m_ObjLookup.retrieve(name);
1.2173 + if (!p_obj)
1.2174 + {
1.2175 + return 0;
1.2176 + }
1.2177 +
1.2178 + topmenu_object_t *obj = *p_obj;
1.2179 + if (obj->type != TopMenuObject_Category)
1.2180 + {
1.2181 + return 0;
1.2182 + }
1.2183 +
1.2184 + return obj->object_id;
1.2185 +}
1.2186 +
1.2187 +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2)
1.2188 +{
1.2189 + obj_by_name_t *obj1 = (obj_by_name_t *)ptr1;
1.2190 + obj_by_name_t *obj2 = (obj_by_name_t *)ptr2;
1.2191 + return strcmp(obj1->name, obj2->name);
1.2192 +}
1.2193 +
1.2194 +unsigned int strncopy(char *dest, const char *src, size_t count)
1.2195 +{
1.2196 + if (!count)
1.2197 + {
1.2198 + return 0;
1.2199 + }
1.2200 +
1.2201 + char *start = dest;
1.2202 + while ((*src) && (--count))
1.2203 + {
1.2204 + *dest++ = *src++;
1.2205 + }
1.2206 + *dest = '\0';
1.2207 +
1.2208 + return (dest - start);
1.2209 +}
1.2210 +
1.2211 +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
1.2212 +{
1.2213 + va_list ap;
1.2214 + va_start(ap, fmt);
1.2215 + size_t len = vsnprintf(buffer, maxlength, fmt, ap);
1.2216 + va_end(ap);
1.2217 +
1.2218 + if (len >= maxlength)
1.2219 + {
1.2220 + buffer[maxlength - 1] = '\0';
1.2221 + return (maxlength - 1);
1.2222 + }
1.2223 + else
1.2224 + {
1.2225 + return len;
1.2226 + }
1.2227 +}
2.1 --- a/extensions/topmenus/TopMenu.h Mon Nov 19 23:11:10 2007 +0000
2.2 +++ b/extensions/topmenus/TopMenu.h Tue Nov 20 02:22:26 2007 +0000
2.3 @@ -1,178 +1,179 @@
2.4 -/**
2.5 - * vim: set ts=4 :
2.6 - * =============================================================================
2.7 - * SourceMod Sample Extension
2.8 - * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
2.9 - * =============================================================================
2.10 - *
2.11 - * This program is free software; you can redistribute it and/or modify it under
2.12 - * the terms of the GNU General Public License, version 3.0, as published by the
2.13 - * Free Software Foundation.
2.14 - *
2.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
2.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.18 - * details.
2.19 - *
2.20 - * You should have received a copy of the GNU General Public License along with
2.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
2.22 - *
2.23 - * As a special exception, AlliedModders LLC gives you permission to link the
2.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
2.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
2.26 - * by the Valve Corporation. You must obey the GNU General Public License in
2.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
2.28 - * this exception to all derivative works. AlliedModders LLC defines further
2.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
2.30 - * or <http://www.sourcemod.net/license.php>.
2.31 - *
2.32 - * Version: $Id$
2.33 - */
2.34 -
2.35 -#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_H_
2.36 -#define _INCLUDE_SOURCEMOD_TOP_MENU_H_
2.37 -
2.38 -#include <sh_list.h>
2.39 -#include <sh_vector.h>
2.40 -#include <sm_trie_tpl.h>
2.41 -#include <ITopMenus.h>
2.42 -#include "smsdk_ext.h"
2.43 -#include "sm_memtable.h"
2.44 -
2.45 -using namespace SourceHook;
2.46 -using namespace SourceMod;
2.47 -
2.48 -struct config_category_t
2.49 -{
2.50 - int name;
2.51 - CVector<int> commands;
2.52 -};
2.53 -
2.54 -struct config_root_t
2.55 -{
2.56 - config_root_t() : strings(1024)
2.57 - {
2.58 - }
2.59 - BaseStringTable strings;
2.60 - CVector<config_category_t *> cats;
2.61 -};
2.62 -
2.63 -struct topmenu_object_t
2.64 -{
2.65 - char name[64]; /** Name */
2.66 - char cmdname[64]; /** Command name */
2.67 - FlagBits flags; /** Admin flags */
2.68 - ITopMenuObjectCallbacks *callbacks; /** Callbacks */
2.69 - IdentityToken_t *owner; /** Owner */
2.70 - unsigned int object_id; /** Object ID */
2.71 - topmenu_object_t *parent; /** Parent, if any */
2.72 - TopMenuObjectType type; /** Object Type */
2.73 - bool is_free; /** Free or not? */
2.74 - char info[255]; /** Info string */
2.75 -};
2.76 -
2.77 -struct topmenu_category_t
2.78 -{
2.79 - CVector<topmenu_object_t *> obj_list; /** Full object list */
2.80 - CVector<topmenu_object_t *> sorted; /** Sorted items */
2.81 - CVector<topmenu_object_t *> unsorted; /** Unsorted items */
2.82 - topmenu_object_t *obj; /** Bound object */
2.83 - unsigned int serial; /** Serial number */
2.84 - bool reorder; /** Whether ordering needs updating */
2.85 -};
2.86 -
2.87 -struct topmenu_player_category_t
2.88 -{
2.89 - IBaseMenu *menu; /** menu pointer */
2.90 - unsigned int serial; /** last known serial */
2.91 -};
2.92 -
2.93 -struct topmenu_player_t
2.94 -{
2.95 - int user_id; /** userid on server */
2.96 - unsigned int menu_serial; /** menu serial no */
2.97 - IBaseMenu *root; /** root menu display */
2.98 - topmenu_player_category_t *cats; /** category display */
2.99 - unsigned int cat_count; /** number of categories */
2.100 - unsigned int last_category; /** last category they selected */
2.101 - unsigned int last_position; /** last position in that category */
2.102 - unsigned int last_root_pos; /** last page in the root menu */
2.103 -};
2.104 -
2.105 -class TopMenu :
2.106 - public ITopMenu,
2.107 - public IMenuHandler,
2.108 - public ITextListener_SMC
2.109 -{
2.110 - friend class TopMenuManager;
2.111 -public:
2.112 - TopMenu(ITopMenuObjectCallbacks *callbacks);
2.113 - ~TopMenu();
2.114 -public: //ITopMenu
2.115 - virtual unsigned int AddToMenu(const char *name,
2.116 - TopMenuObjectType type,
2.117 - ITopMenuObjectCallbacks *callbacks,
2.118 - IdentityToken_t *owner,
2.119 - const char *cmdname,
2.120 - FlagBits flags,
2.121 - unsigned int parent);
2.122 - unsigned int AddToMenu2(const char *name,
2.123 - TopMenuObjectType type,
2.124 - ITopMenuObjectCallbacks *callbacks,
2.125 - IdentityToken_t *owner,
2.126 - const char *cmdname,
2.127 - FlagBits flags,
2.128 - unsigned int parent,
2.129 - const char *info_string);
2.130 - virtual void RemoveFromMenu(unsigned int object_id);
2.131 - virtual bool DisplayMenu(int client,
2.132 - unsigned int hold_time,
2.133 - TopMenuPosition position);
2.134 - virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength);
2.135 - virtual unsigned int FindCategory(const char *name);
2.136 - const char *GetObjectInfoString(unsigned int object_id);
2.137 -public: //IMenuHandler
2.138 - virtual void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
2.139 - virtual void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
2.140 - virtual unsigned int OnMenuDisplayItem(IBaseMenu *menu,
2.141 - int client,
2.142 - IMenuPanel *panel,
2.143 - unsigned int item,
2.144 - const ItemDrawInfo &dr);
2.145 - virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
2.146 -public: //ITextListener_SMC
2.147 - void ReadSMC_ParseStart();
2.148 - SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
2.149 - SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
2.150 - SMCResult ReadSMC_LeavingSection(const SMCStates *states);
2.151 -private:
2.152 - void SortCategoriesIfNeeded();
2.153 - void SortCategoryIfNeeded(unsigned int category);
2.154 -private:
2.155 - bool DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position);
2.156 - void CreatePlayers(int max_clients);
2.157 - void UpdateClientRoot(int client, IGamePlayer *pGamePlayer=NULL);
2.158 - void UpdateClientCategory(int client, unsigned int category);
2.159 - void TearDownClient(topmenu_player_t *player);
2.160 -private:
2.161 - void OnClientConnected(int client);
2.162 - void OnClientDisconnected(int client);
2.163 - void OnServerActivated(int max_clients);
2.164 - bool OnIdentityRemoval(IdentityToken_t *owner);
2.165 -private:
2.166 - config_root_t m_Config; /* Configuration from file */
2.167 - topmenu_player_t *m_clients; /* Client array */
2.168 - CVector<unsigned int> m_SortedCats; /* Sorted categories */
2.169 - CVector<unsigned int> m_UnsortedCats; /* Un-sorted categories */
2.170 - CVector<topmenu_category_t *> m_Categories; /* Category array */
2.171 - CVector<topmenu_object_t *> m_Objects; /* Object array */
2.172 - KTrie<topmenu_object_t *> m_ObjLookup; /* Object lookup trie */
2.173 - unsigned int m_SerialNo; /* Serial number for updating */
2.174 - ITopMenuObjectCallbacks *m_pTitle; /* Title callbacks */
2.175 - int m_max_clients; /* Maximum number of clients */
2.176 - bool m_bCatsNeedResort; /* True if categories need a resort */
2.177 -};
2.178 -
2.179 -unsigned int strncopy(char *dest, const char *src, size_t count);
2.180 -
2.181 -#endif //_INCLUDE_SOURCEMOD_TOP_MENU_H_
2.182 +/**
2.183 + * vim: set ts=4 :
2.184 + * =============================================================================
2.185 + * SourceMod Sample Extension
2.186 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
2.187 + * =============================================================================
2.188 + *
2.189 + * This program is free software; you can redistribute it and/or modify it under
2.190 + * the terms of the GNU General Public License, version 3.0, as published by the
2.191 + * Free Software Foundation.
2.192 + *
2.193 + * This program is distributed in the hope that it will be useful, but WITHOUT
2.194 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.195 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.196 + * details.
2.197 + *
2.198 + * You should have received a copy of the GNU General Public License along with
2.199 + * this program. If not, see <http://www.gnu.org/licenses/>.
2.200 + *
2.201 + * As a special exception, AlliedModders LLC gives you permission to link the
2.202 + * code of this program (as well as its derivative works) to "Half-Life 2," the
2.203 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
2.204 + * by the Valve Corporation. You must obey the GNU General Public License in
2.205 + * all respects for all other code used. Additionally, AlliedModders LLC grants
2.206 + * this exception to all derivative works. AlliedModders LLC defines further
2.207 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
2.208 + * or <http://www.sourcemod.net/license.php>.
2.209 + *
2.210 + * Version: $Id$
2.211 + */
2.212 +
2.213 +#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_H_
2.214 +#define _INCLUDE_SOURCEMOD_TOP_MENU_H_
2.215 +
2.216 +#include <sh_list.h>
2.217 +#include <sh_vector.h>
2.218 +#include <sm_trie_tpl.h>
2.219 +#include <ITopMenus.h>
2.220 +#include "smsdk_ext.h"
2.221 +#include "sm_memtable.h"
2.222 +
2.223 +using namespace SourceHook;
2.224 +using namespace SourceMod;
2.225 +
2.226 +struct config_category_t
2.227 +{
2.228 + int name;
2.229 + CVector<int> commands;
2.230 +};
2.231 +
2.232 +struct config_root_t
2.233 +{
2.234 + config_root_t() : strings(1024)
2.235 + {
2.236 + }
2.237 + BaseStringTable strings;
2.238 + CVector<config_category_t *> cats;
2.239 +};
2.240 +
2.241 +struct topmenu_object_t
2.242 +{
2.243 + char name[64]; /** Name */
2.244 + char cmdname[64]; /** Command name */
2.245 + FlagBits flags; /** Admin flags */
2.246 + ITopMenuObjectCallbacks *callbacks; /** Callbacks */
2.247 + IdentityToken_t *owner; /** Owner */
2.248 + unsigned int object_id; /** Object ID */
2.249 + topmenu_object_t *parent; /** Parent, if any */
2.250 + TopMenuObjectType type; /** Object Type */
2.251 + bool is_free; /** Free or not? */
2.252 + char info[255]; /** Info string */
2.253 +};
2.254 +
2.255 +struct topmenu_category_t
2.256 +{
2.257 + CVector<topmenu_object_t *> obj_list; /** Full object list */
2.258 + CVector<topmenu_object_t *> sorted; /** Sorted items */
2.259 + CVector<topmenu_object_t *> unsorted; /** Unsorted items */
2.260 + topmenu_object_t *obj; /** Bound object */
2.261 + unsigned int serial; /** Serial number */
2.262 + bool reorder; /** Whether ordering needs updating */
2.263 +};
2.264 +
2.265 +struct topmenu_player_category_t
2.266 +{
2.267 + IBaseMenu *menu; /** menu pointer */
2.268 + unsigned int serial; /** last known serial */
2.269 +};
2.270 +
2.271 +struct topmenu_player_t
2.272 +{
2.273 + int user_id; /** userid on server */
2.274 + unsigned int menu_serial; /** menu serial no */
2.275 + IBaseMenu *root; /** root menu display */
2.276 + topmenu_player_category_t *cats; /** category display */
2.277 + unsigned int cat_count; /** number of categories */
2.278 + unsigned int last_category; /** last category they selected */
2.279 + unsigned int last_position; /** last position in that category */
2.280 + unsigned int last_root_pos; /** last page in the root menu */
2.281 +};
2.282 +
2.283 +class TopMenu :
2.284 + public ITopMenu,
2.285 + public IMenuHandler,
2.286 + public ITextListener_SMC
2.287 +{
2.288 + friend class TopMenuManager;
2.289 +public:
2.290 + TopMenu(ITopMenuObjectCallbacks *callbacks);
2.291 + ~TopMenu();
2.292 +public: //ITopMenu
2.293 + virtual unsigned int AddToMenu(const char *name,
2.294 + TopMenuObjectType type,
2.295 + ITopMenuObjectCallbacks *callbacks,
2.296 + IdentityToken_t *owner,
2.297 + const char *cmdname,
2.298 + FlagBits flags,
2.299 + unsigned int parent);
2.300 + unsigned int AddToMenu2(const char *name,
2.301 + TopMenuObjectType type,
2.302 + ITopMenuObjectCallbacks *callbacks,
2.303 + IdentityToken_t *owner,
2.304 + const char *cmdname,
2.305 + FlagBits flags,
2.306 + unsigned int parent,
2.307 + const char *info_string);
2.308 + virtual void RemoveFromMenu(unsigned int object_id);
2.309 + virtual bool DisplayMenu(int client,
2.310 + unsigned int hold_time,
2.311 + TopMenuPosition position);
2.312 + virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength);
2.313 + virtual unsigned int FindCategory(const char *name);
2.314 + const char *GetObjectInfoString(unsigned int object_id);
2.315 + const char *GetObjectName(unsigned int object_id);
2.316 +public: //IMenuHandler
2.317 + virtual void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
2.318 + virtual void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
2.319 + virtual unsigned int OnMenuDisplayItem(IBaseMenu *menu,
2.320 + int client,
2.321 + IMenuPanel *panel,
2.322 + unsigned int item,
2.323 + const ItemDrawInfo &dr);
2.324 + virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
2.325 +public: //ITextListener_SMC
2.326 + void ReadSMC_ParseStart();
2.327 + SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
2.328 + SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
2.329 + SMCResult ReadSMC_LeavingSection(const SMCStates *states);
2.330 +private:
2.331 + void SortCategoriesIfNeeded();
2.332 + void SortCategoryIfNeeded(unsigned int category);
2.333 +private:
2.334 + bool DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position);
2.335 + void CreatePlayers(int max_clients);
2.336 + void UpdateClientRoot(int client, IGamePlayer *pGamePlayer=NULL);
2.337 + void UpdateClientCategory(int client, unsigned int category);
2.338 + void TearDownClient(topmenu_player_t *player);
2.339 +private:
2.340 + void OnClientConnected(int client);
2.341 + void OnClientDisconnected(int client);
2.342 + void OnServerActivated(int max_clients);
2.343 + bool OnIdentityRemoval(IdentityToken_t *owner);
2.344 +private:
2.345 + config_root_t m_Config; /* Configuration from file */
2.346 + topmenu_player_t *m_clients; /* Client array */
2.347 + CVector<unsigned int> m_SortedCats; /* Sorted categories */
2.348 + CVector<unsigned int> m_UnsortedCats; /* Un-sorted categories */
2.349 + CVector<topmenu_category_t *> m_Categories; /* Category array */
2.350 + CVector<topmenu_object_t *> m_Objects; /* Object array */
2.351 + KTrie<topmenu_object_t *> m_ObjLookup; /* Object lookup trie */
2.352 + unsigned int m_SerialNo; /* Serial number for updating */
2.353 + ITopMenuObjectCallbacks *m_pTitle; /* Title callbacks */
2.354 + int m_max_clients; /* Maximum number of clients */
2.355 + bool m_bCatsNeedResort; /* True if categories need a resort */
2.356 +};
2.357 +
2.358 +unsigned int strncopy(char *dest, const char *src, size_t count);
2.359 +
2.360 +#endif //_INCLUDE_SOURCEMOD_TOP_MENU_H_
3.1 --- a/extensions/topmenus/smn_topmenus.cpp Mon Nov 19 23:11:10 2007 +0000
3.2 +++ b/extensions/topmenus/smn_topmenus.cpp Tue Nov 20 02:22:26 2007 +0000
3.3 @@ -1,358 +1,383 @@
3.4 -/**
3.5 - * vim: set ts=4 :
3.6 - * =============================================================================
3.7 - * SourceMod Sample Extension
3.8 - * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
3.9 - * =============================================================================
3.10 - *
3.11 - * This program is free software; you can redistribute it and/or modify it under
3.12 - * the terms of the GNU General Public License, version 3.0, as published by the
3.13 - * Free Software Foundation.
3.14 - *
3.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
3.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.18 - * details.
3.19 - *
3.20 - * You should have received a copy of the GNU General Public License along with
3.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
3.22 - *
3.23 - * As a special exception, AlliedModders LLC gives you permission to link the
3.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
3.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
3.26 - * by the Valve Corporation. You must obey the GNU General Public License in
3.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
3.28 - * this exception to all derivative works. AlliedModders LLC defines further
3.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
3.30 - * or <http://www.sourcemod.net/license.php>.
3.31 - *
3.32 - * Version: $Id$
3.33 - */
3.34 -
3.35 -#include "extension.h"
3.36 -#include "TopMenuManager.h"
3.37 -#include "TopMenu.h"
3.38 -
3.39 -HandleType_t hTopMenuType;
3.40 -
3.41 -class TopMenuHandle : public IHandleTypeDispatch
3.42 -{
3.43 -public:
3.44 - void OnHandleDestroy(HandleType_t type, void *object)
3.45 - {
3.46 - ITopMenu *pTopMenu = (ITopMenu *)object;
3.47 - g_TopMenus.DestroyTopMenu(pTopMenu);
3.48 - }
3.49 -} s_TopMenuHandle;
3.50 -
3.51 -void Initialize_Natives()
3.52 -{
3.53 - hTopMenuType = handlesys->CreateType("ITopMenu",
3.54 - &s_TopMenuHandle,
3.55 - 0,
3.56 - NULL,
3.57 - NULL,
3.58 - myself->GetIdentity(),
3.59 - NULL);
3.60 -}
3.61 -
3.62 -void Shutdown_Natives()
3.63 -{
3.64 - handlesys->RemoveType(hTopMenuType, myself->GetIdentity());
3.65 -}
3.66 -
3.67 -enum TopMenuAction
3.68 -{
3.69 - TopMenuAction_DisplayOption = 0,
3.70 - TopMenuAction_DisplayTitle = 1,
3.71 - TopMenuAction_SelectOption = 2,
3.72 - TopMenuAction_DrawOption = 3,
3.73 - TopMenuAction_RemoveObject = 4,
3.74 -};
3.75 -
3.76 -class TopMenuCallbacks : public ITopMenuObjectCallbacks
3.77 -{
3.78 -public:
3.79 - TopMenuCallbacks(IPluginFunction *pFunction) : m_pFunction(pFunction)
3.80 - {
3.81 - }
3.82 -
3.83 - unsigned int OnTopMenuDrawOption(ITopMenu *menu,
3.84 - int client,
3.85 - unsigned int object_id)
3.86 - {
3.87 - char buffer[2] = {ITEMDRAW_DEFAULT, 0x0};
3.88 - m_pFunction->PushCell(m_hMenuHandle);
3.89 - m_pFunction->PushCell(TopMenuAction_DrawOption);
3.90 - m_pFunction->PushCell(object_id);
3.91 - m_pFunction->PushCell(client);
3.92 - m_pFunction->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
3.93 - m_pFunction->PushCell(sizeof(buffer));
3.94 - m_pFunction->Execute(NULL);
3.95 - return (unsigned int)buffer[0];
3.96 - }
3.97 -
3.98 - void OnTopMenuDisplayOption(ITopMenu *menu,
3.99 - int client,
3.100 - unsigned int object_id,
3.101 - char buffer[],
3.102 - size_t maxlength)
3.103 - {
3.104 - m_pFunction->PushCell(m_hMenuHandle);
3.105 - m_pFunction->PushCell(TopMenuAction_DisplayOption);
3.106 - m_pFunction->PushCell(object_id);
3.107 - m_pFunction->PushCell(client);
3.108 - m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
3.109 - m_pFunction->PushCell(maxlength);
3.110 - m_pFunction->Execute(NULL);
3.111 - }
3.112 -
3.113 - void OnTopMenuDisplayTitle(ITopMenu *menu,
3.114 - int client,
3.115 - unsigned int object_id,
3.116 - char buffer[],
3.117 - size_t maxlength)
3.118 - {
3.119 - m_pFunction->PushCell(m_hMenuHandle);
3.120 - m_pFunction->PushCell(TopMenuAction_DisplayTitle);
3.121 - m_pFunction->PushCell(object_id);
3.122 - m_pFunction->PushCell(client);
3.123 - m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
3.124 - m_pFunction->PushCell(maxlength);
3.125 - m_pFunction->Execute(NULL);
3.126 - }
3.127 -
3.128 - void OnTopMenuSelectOption(ITopMenu *menu,
3.129 - int client,
3.130 - unsigned int object_id)
3.131 - {
3.132 - unsigned int old_reply = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
3.133 - m_pFunction->PushCell(m_hMenuHandle);
3.134 - m_pFunction->PushCell(TopMenuAction_SelectOption);
3.135 - m_pFunction->PushCell(object_id);
3.136 - m_pFunction->PushCell(client);
3.137 - m_pFunction->PushString("");
3.138 - m_pFunction->PushCell(0);
3.139 - m_pFunction->Execute(NULL);
3.140 - playerhelpers->SetReplyTo(old_reply);
3.141 - }
3.142 -
3.143 - void OnTopMenuObjectRemoved(ITopMenu *menu, unsigned int object_id)
3.144 - {
3.145 - m_pFunction->PushCell(m_hMenuHandle);
3.146 - m_pFunction->PushCell(TopMenuAction_RemoveObject);
3.147 - m_pFunction->PushCell(object_id);
3.148 - m_pFunction->PushCell(0);
3.149 - m_pFunction->PushString("");
3.150 - m_pFunction->PushCell(0);
3.151 - m_pFunction->Execute(NULL);
3.152 -
3.153 - delete this;
3.154 - }
3.155 -
3.156 - Handle_t m_hMenuHandle;
3.157 - IPluginFunction *m_pFunction;
3.158 -};
3.159 -
3.160 -static cell_t CreateTopMenu(IPluginContext *pContext, const cell_t *params)
3.161 -{
3.162 - IPluginFunction *func = pContext->GetFunctionById(params[1]);
3.163 - if (func == NULL)
3.164 - {
3.165 - return pContext ->ThrowNativeError("Invalid function specified");
3.166 - }
3.167 -
3.168 - TopMenuCallbacks *cb = new TopMenuCallbacks(func);
3.169 -
3.170 - ITopMenu *pMenu = g_TopMenus.CreateTopMenu(cb);
3.171 -
3.172 - if (!pMenu)
3.173 - {
3.174 - delete cb;
3.175 - return BAD_HANDLE;
3.176 - }
3.177 -
3.178 - Handle_t hndl = handlesys->CreateHandle(hTopMenuType,
3.179 - pMenu,
3.180 - pContext->GetIdentity(),
3.181 - myself->GetIdentity(),
3.182 - NULL);
3.183 - if (hndl == 0)
3.184 - {
3.185 - g_TopMenus.DestroyTopMenu(pMenu);
3.186 - return BAD_HANDLE;
3.187 - }
3.188 -
3.189 - cb->m_hMenuHandle = hndl;
3.190 -
3.191 - return hndl;
3.192 -}
3.193 -
3.194 -static cell_t LoadTopMenuConfig(IPluginContext *pContext, const cell_t *params)
3.195 -{
3.196 - HandleError err;
3.197 - ITopMenu *pMenu;
3.198 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.199 -
3.200 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.201 - != HandleError_None)
3.202 - {
3.203 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.204 - }
3.205 -
3.206 - char *file, *err_buf;
3.207 - pContext->LocalToString(params[2], &file);
3.208 - pContext->LocalToString(params[3], &err_buf);
3.209 -
3.210 - char path[PLATFORM_MAX_PATH];
3.211 - g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
3.212 -
3.213 - return pMenu->LoadConfiguration(path, err_buf, params[4]) ? 1 : 0;
3.214 -}
3.215 -
3.216 -static cell_t AddToTopMenu(IPluginContext *pContext, const cell_t *params)
3.217 -{
3.218 - HandleError err;
3.219 - ITopMenu *pMenu;
3.220 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.221 -
3.222 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.223 - != HandleError_None)
3.224 - {
3.225 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.226 - }
3.227 -
3.228 - IPluginFunction *func = pContext->GetFunctionById(params[4]);
3.229 - if (func == NULL)
3.230 - {
3.231 - return pContext ->ThrowNativeError("Invalid function specified");
3.232 - }
3.233 -
3.234 - TopMenuCallbacks *cb = new TopMenuCallbacks(func);
3.235 -
3.236 - char *name, *cmdname, *info_string = NULL;
3.237 - pContext->LocalToString(params[2], &name);
3.238 - pContext->LocalToString(params[6], &cmdname);
3.239 -
3.240 - if (params[0] >= 8)
3.241 - {
3.242 - pContext->LocalToString(params[8], &info_string);
3.243 - }
3.244 -
3.245 - TopMenuObjectType obj_type = (TopMenuObjectType)params[3];
3.246 -
3.247 - unsigned int object_id;
3.248 - if ((object_id = pMenu->AddToMenu2(name,
3.249 - obj_type,
3.250 - cb,
3.251 - pContext->GetIdentity(),
3.252 - cmdname,
3.253 - params[7],
3.254 - params[5],
3.255 - info_string)) == 0)
3.256 - {
3.257 - delete cb;
3.258 - return 0;
3.259 - }
3.260 -
3.261 - cb->m_hMenuHandle = params[1];
3.262 -
3.263 - return object_id;
3.264 -}
3.265 -
3.266 -static cell_t RemoveFromTopMenu(IPluginContext *pContext, const cell_t *params)
3.267 -{
3.268 - HandleError err;
3.269 - ITopMenu *pMenu;
3.270 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.271 -
3.272 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.273 - != HandleError_None)
3.274 - {
3.275 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.276 - }
3.277 -
3.278 - pMenu->RemoveFromMenu(params[2]);
3.279 -
3.280 - return 1;
3.281 -}
3.282 -
3.283 -static cell_t FindTopMenuCategory(IPluginContext *pContext, const cell_t *params)
3.284 -{
3.285 - HandleError err;
3.286 - ITopMenu *pMenu;
3.287 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.288 -
3.289 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.290 - != HandleError_None)
3.291 - {
3.292 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.293 - }
3.294 -
3.295 - char *name;
3.296 - pContext->LocalToString(params[2], &name);
3.297 -
3.298 - return pMenu->FindCategory(name);
3.299 -}
3.300 -
3.301 -static cell_t DisplayTopMenu(IPluginContext *pContext, const cell_t *params)
3.302 -{
3.303 - HandleError err;
3.304 - ITopMenu *pMenu;
3.305 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.306 -
3.307 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.308 - != HandleError_None)
3.309 - {
3.310 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.311 - }
3.312 -
3.313 - int client = params[2];
3.314 - IGamePlayer *player = playerhelpers->GetGamePlayer(client);
3.315 - if (!player)
3.316 - {
3.317 - return pContext->ThrowNativeError("Invalid client index %d", client);
3.318 - }
3.319 - else if (!player->IsInGame())
3.320 - {
3.321 - return pContext->ThrowNativeError("Client %d is not in game", client);
3.322 - }
3.323 -
3.324 - return pMenu->DisplayMenu(client, 0, (TopMenuPosition)params[3]);
3.325 -}
3.326 -
3.327 -static cell_t GetTopMenuInfoString(IPluginContext *pContext, const cell_t *params)
3.328 -{
3.329 - HandleError err;
3.330 - ITopMenu *pMenu;
3.331 - HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.332 -
3.333 - if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.334 - != HandleError_None)
3.335 - {
3.336 - return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.337 - }
3.338 -
3.339 - const char *str;
3.340 - if ((str = pMenu->GetObjectInfoString(params[2])) == NULL)
3.341 - {
3.342 - return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
3.343 - }
3.344 -
3.345 - char *buffer;
3.346 - pContext->LocalToString(params[3], &buffer);
3.347 -
3.348 - return strncopy(buffer, str, params[4]);
3.349 -}
3.350 -
3.351 -sp_nativeinfo_t g_TopMenuNatives[] =
3.352 -{
3.353 - {"AddToTopMenu", AddToTopMenu},
3.354 - {"CreateTopMenu", CreateTopMenu},
3.355 - {"DisplayTopMenu", DisplayTopMenu},
3.356 - {"LoadTopMenuConfig", LoadTopMenuConfig},
3.357 - {"RemoveFromTopMenu", RemoveFromTopMenu},
3.358 - {"FindTopMenuCategory", FindTopMenuCategory},
3.359 - {"GetTopMenuInfoString", GetTopMenuInfoString},
3.360 - {NULL, NULL},
3.361 -};
3.362 +/**
3.363 + * vim: set ts=4 :
3.364 + * =============================================================================
3.365 + * SourceMod Sample Extension
3.366 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
3.367 + * =============================================================================
3.368 + *
3.369 + * This program is free software; you can redistribute it and/or modify it under
3.370 + * the terms of the GNU General Public License, version 3.0, as published by the
3.371 + * Free Software Foundation.
3.372 + *
3.373 + * This program is distributed in the hope that it will be useful, but WITHOUT
3.374 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
3.375 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
3.376 + * details.
3.377 + *
3.378 + * You should have received a copy of the GNU General Public License along with
3.379 + * this program. If not, see <http://www.gnu.org/licenses/>.
3.380 + *
3.381 + * As a special exception, AlliedModders LLC gives you permission to link the
3.382 + * code of this program (as well as its derivative works) to "Half-Life 2," the
3.383 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
3.384 + * by the Valve Corporation. You must obey the GNU General Public License in
3.385 + * all respects for all other code used. Additionally, AlliedModders LLC grants
3.386 + * this exception to all derivative works. AlliedModders LLC defines further
3.387 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
3.388 + * or <http://www.sourcemod.net/license.php>.
3.389 + *
3.390 + * Version: $Id$
3.391 + */
3.392 +
3.393 +#include "extension.h"
3.394 +#include "TopMenuManager.h"
3.395 +#include "TopMenu.h"
3.396 +
3.397 +HandleType_t hTopMenuType;
3.398 +
3.399 +class TopMenuHandle : public IHandleTypeDispatch
3.400 +{
3.401 +public:
3.402 + void OnHandleDestroy(HandleType_t type, void *object)
3.403 + {
3.404 + ITopMenu *pTopMenu = (ITopMenu *)object;
3.405 + g_TopMenus.DestroyTopMenu(pTopMenu);
3.406 + }
3.407 +} s_TopMenuHandle;
3.408 +
3.409 +void Initialize_Natives()
3.410 +{
3.411 + hTopMenuType = handlesys->CreateType("ITopMenu",
3.412 + &s_TopMenuHandle,
3.413 + 0,
3.414 + NULL,
3.415 + NULL,
3.416 + myself->GetIdentity(),
3.417 + NULL);
3.418 +}
3.419 +
3.420 +void Shutdown_Natives()
3.421 +{
3.422 + handlesys->RemoveType(hTopMenuType, myself->GetIdentity());
3.423 +}
3.424 +
3.425 +enum TopMenuAction
3.426 +{
3.427 + TopMenuAction_DisplayOption = 0,
3.428 + TopMenuAction_DisplayTitle = 1,
3.429 + TopMenuAction_SelectOption = 2,
3.430 + TopMenuAction_DrawOption = 3,
3.431 + TopMenuAction_RemoveObject = 4,
3.432 +};
3.433 +
3.434 +class TopMenuCallbacks : public ITopMenuObjectCallbacks
3.435 +{
3.436 +public:
3.437 + TopMenuCallbacks(IPluginFunction *pFunction) : m_pFunction(pFunction)
3.438 + {
3.439 + }
3.440 +
3.441 + unsigned int OnTopMenuDrawOption(ITopMenu *menu,
3.442 + int client,
3.443 + unsigned int object_id)
3.444 + {
3.445 + char buffer[2] = {ITEMDRAW_DEFAULT, 0x0};
3.446 + m_pFunction->PushCell(m_hMenuHandle);
3.447 + m_pFunction->PushCell(TopMenuAction_DrawOption);
3.448 + m_pFunction->PushCell(object_id);
3.449 + m_pFunction->PushCell(client);
3.450 + m_pFunction->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
3.451 + m_pFunction->PushCell(sizeof(buffer));
3.452 + m_pFunction->Execute(NULL);
3.453 + return (unsigned int)buffer[0];
3.454 + }
3.455 +
3.456 + void OnTopMenuDisplayOption(ITopMenu *menu,
3.457 + int client,
3.458 + unsigned int object_id,
3.459 + char buffer[],
3.460 + size_t maxlength)
3.461 + {
3.462 + m_pFunction->PushCell(m_hMenuHandle);
3.463 + m_pFunction->PushCell(TopMenuAction_DisplayOption);
3.464 + m_pFunction->PushCell(object_id);
3.465 + m_pFunction->PushCell(client);
3.466 + m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
3.467 + m_pFunction->PushCell(maxlength);
3.468 + m_pFunction->Execute(NULL);
3.469 + }
3.470 +
3.471 + void OnTopMenuDisplayTitle(ITopMenu *menu,
3.472 + int client,
3.473 + unsigned int object_id,
3.474 + char buffer[],
3.475 + size_t maxlength)
3.476 + {
3.477 + m_pFunction->PushCell(m_hMenuHandle);
3.478 + m_pFunction->PushCell(TopMenuAction_DisplayTitle);
3.479 + m_pFunction->PushCell(object_id);
3.480 + m_pFunction->PushCell(client);
3.481 + m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
3.482 + m_pFunction->PushCell(maxlength);
3.483 + m_pFunction->Execute(NULL);
3.484 + }
3.485 +
3.486 + void OnTopMenuSelectOption(ITopMenu *menu,
3.487 + int client,
3.488 + unsigned int object_id)
3.489 + {
3.490 + unsigned int old_reply = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
3.491 + m_pFunction->PushCell(m_hMenuHandle);
3.492 + m_pFunction->PushCell(TopMenuAction_SelectOption);
3.493 + m_pFunction->PushCell(object_id);
3.494 + m_pFunction->PushCell(client);
3.495 + m_pFunction->PushString("");
3.496 + m_pFunction->PushCell(0);
3.497 + m_pFunction->Execute(NULL);
3.498 + playerhelpers->SetReplyTo(old_reply);
3.499 + }
3.500 +
3.501 + void OnTopMenuObjectRemoved(ITopMenu *menu, unsigned int object_id)
3.502 + {
3.503 + m_pFunction->PushCell(m_hMenuHandle);
3.504 + m_pFunction->PushCell(TopMenuAction_RemoveObject);
3.505 + m_pFunction->PushCell(object_id);
3.506 + m_pFunction->PushCell(0);
3.507 + m_pFunction->PushString("");
3.508 + m_pFunction->PushCell(0);
3.509 + m_pFunction->Execute(NULL);
3.510 +
3.511 + delete this;
3.512 + }
3.513 +
3.514 + Handle_t m_hMenuHandle;
3.515 + IPluginFunction *m_pFunction;
3.516 +};
3.517 +
3.518 +static cell_t CreateTopMenu(IPluginContext *pContext, const cell_t *params)
3.519 +{
3.520 + IPluginFunction *func = pContext->GetFunctionById(params[1]);
3.521 + if (func == NULL)
3.522 + {
3.523 + return pContext ->ThrowNativeError("Invalid function specified");
3.524 + }
3.525 +
3.526 + TopMenuCallbacks *cb = new TopMenuCallbacks(func);
3.527 +
3.528 + ITopMenu *pMenu = g_TopMenus.CreateTopMenu(cb);
3.529 +
3.530 + if (!pMenu)
3.531 + {
3.532 + delete cb;
3.533 + return BAD_HANDLE;
3.534 + }
3.535 +
3.536 + Handle_t hndl = handlesys->CreateHandle(hTopMenuType,
3.537 + pMenu,
3.538 + pContext->GetIdentity(),
3.539 + myself->GetIdentity(),
3.540 + NULL);
3.541 + if (hndl == 0)
3.542 + {
3.543 + g_TopMenus.DestroyTopMenu(pMenu);
3.544 + return BAD_HANDLE;
3.545 + }
3.546 +
3.547 + cb->m_hMenuHandle = hndl;
3.548 +
3.549 + return hndl;
3.550 +}
3.551 +
3.552 +static cell_t LoadTopMenuConfig(IPluginContext *pContext, const cell_t *params)
3.553 +{
3.554 + HandleError err;
3.555 + ITopMenu *pMenu;
3.556 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.557 +
3.558 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.559 + != HandleError_None)
3.560 + {
3.561 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.562 + }
3.563 +
3.564 + char *file, *err_buf;
3.565 + pContext->LocalToString(params[2], &file);
3.566 + pContext->LocalToString(params[3], &err_buf);
3.567 +
3.568 + char path[PLATFORM_MAX_PATH];
3.569 + g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
3.570 +
3.571 + return pMenu->LoadConfiguration(path, err_buf, params[4]) ? 1 : 0;
3.572 +}
3.573 +
3.574 +static cell_t AddToTopMenu(IPluginContext *pContext, const cell_t *params)
3.575 +{
3.576 + HandleError err;
3.577 + ITopMenu *pMenu;
3.578 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.579 +
3.580 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.581 + != HandleError_None)
3.582 + {
3.583 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.584 + }
3.585 +
3.586 + IPluginFunction *func = pContext->GetFunctionById(params[4]);
3.587 + if (func == NULL)
3.588 + {
3.589 + return pContext ->ThrowNativeError("Invalid function specified");
3.590 + }
3.591 +
3.592 + TopMenuCallbacks *cb = new TopMenuCallbacks(func);
3.593 +
3.594 + char *name, *cmdname, *info_string = NULL;
3.595 + pContext->LocalToString(params[2], &name);
3.596 + pContext->LocalToString(params[6], &cmdname);
3.597 +
3.598 + if (params[0] >= 8)
3.599 + {
3.600 + pContext->LocalToString(params[8], &info_string);
3.601 + }
3.602 +
3.603 + TopMenuObjectType obj_type = (TopMenuObjectType)params[3];
3.604 +
3.605 + unsigned int object_id;
3.606 + if ((object_id = pMenu->AddToMenu2(name,
3.607 + obj_type,
3.608 + cb,
3.609 + pContext->GetIdentity(),
3.610 + cmdname,
3.611 + params[7],
3.612 + params[5],
3.613 + info_string)) == 0)
3.614 + {
3.615 + delete cb;
3.616 + return 0;
3.617 + }
3.618 +
3.619 + cb->m_hMenuHandle = params[1];
3.620 +
3.621 + return object_id;
3.622 +}
3.623 +
3.624 +static cell_t RemoveFromTopMenu(IPluginContext *pContext, const cell_t *params)
3.625 +{
3.626 + HandleError err;
3.627 + ITopMenu *pMenu;
3.628 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.629 +
3.630 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.631 + != HandleError_None)
3.632 + {
3.633 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.634 + }
3.635 +
3.636 + pMenu->RemoveFromMenu(params[2]);
3.637 +
3.638 + return 1;
3.639 +}
3.640 +
3.641 +static cell_t FindTopMenuCategory(IPluginContext *pContext, const cell_t *params)
3.642 +{
3.643 + HandleError err;
3.644 + ITopMenu *pMenu;
3.645 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.646 +
3.647 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.648 + != HandleError_None)
3.649 + {
3.650 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.651 + }
3.652 +
3.653 + char *name;
3.654 + pContext->LocalToString(params[2], &name);
3.655 +
3.656 + return pMenu->FindCategory(name);
3.657 +}
3.658 +
3.659 +static cell_t DisplayTopMenu(IPluginContext *pContext, const cell_t *params)
3.660 +{
3.661 + HandleError err;
3.662 + ITopMenu *pMenu;
3.663 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.664 +
3.665 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.666 + != HandleError_None)
3.667 + {
3.668 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.669 + }
3.670 +
3.671 + int client = params[2];
3.672 + IGamePlayer *player = playerhelpers->GetGamePlayer(client);
3.673 + if (!player)
3.674 + {
3.675 + return pContext->ThrowNativeError("Invalid client index %d", client);
3.676 + }
3.677 + else if (!player->IsInGame())
3.678 + {
3.679 + return pContext->ThrowNativeError("Client %d is not in game", client);
3.680 + }
3.681 +
3.682 + return pMenu->DisplayMenu(client, 0, (TopMenuPosition)params[3]);
3.683 +}
3.684 +
3.685 +static cell_t GetTopMenuInfoString(IPluginContext *pContext, const cell_t *params)
3.686 +{
3.687 + HandleError err;
3.688 + ITopMenu *pMenu;
3.689 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.690 +
3.691 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.692 + != HandleError_None)
3.693 + {
3.694 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.695 + }
3.696 +
3.697 + const char *str;
3.698 + if ((str = pMenu->GetObjectInfoString(params[2])) == NULL)
3.699 + {
3.700 + return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
3.701 + }
3.702 +
3.703 + char *buffer;
3.704 + pContext->LocalToString(params[3], &buffer);
3.705 +
3.706 + return strncopy(buffer, str, params[4]);
3.707 +}
3.708 +
3.709 +static cell_t GetTopMenuName(IPluginContext *pContext, const cell_t *params)
3.710 +{
3.711 + HandleError err;
3.712 + ITopMenu *pMenu;
3.713 + HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
3.714 +
3.715 + if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
3.716 + != HandleError_None)
3.717 + {
3.718 + return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
3.719 + }
3.720 +
3.721 + const char *str;
3.722 + if ((str = pMenu->GetObjectName(params[2])) == NULL)
3.723 + {
3.724 + return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
3.725 + }
3.726 +
3.727 + char *buffer;
3.728 + pContext->LocalToString(params[3], &buffer);
3.729 +
3.730 + return strncopy(buffer, str, params[4]);
3.731 +}
3.732 +
3.733 +sp_nativeinfo_t g_TopMenuNatives[] =
3.734 +{
3.735 + {"AddToTopMenu", AddToTopMenu},
3.736 + {"CreateTopMenu", CreateTopMenu},
3.737 + {"DisplayTopMenu", DisplayTopMenu},
3.738 + {"LoadTopMenuConfig", LoadTopMenuConfig},
3.739 + {"RemoveFromTopMenu", RemoveFromTopMenu},
3.740 + {"FindTopMenuCategory", FindTopMenuCategory},
3.741 + {"GetTopMenuInfoString", GetTopMenuInfoString},
3.742 + {"GetTopMenuName", GetTopMenuName},
3.743 + {NULL, NULL},
3.744 +};
4.1 --- a/plugins/include/topmenus.inc Mon Nov 19 23:11:10 2007 +0000
4.2 +++ b/plugins/include/topmenus.inc Tue Nov 20 02:22:26 2007 +0000
4.3 @@ -1,277 +1,290 @@
4.4 -/**
4.5 - * vim: set ts=4 :
4.6 - * =============================================================================
4.7 - * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
4.8 - * =============================================================================
4.9 - *
4.10 - * This file is part of the SourceMod/SourcePawn SDK.
4.11 - *
4.12 - * This program is free software; you can redistribute it and/or modify it under
4.13 - * the terms of the GNU General Public License, version 3.0, as published by the
4.14 - * Free Software Foundation.
4.15 - *
4.16 - * This program is distributed in the hope that it will be useful, but WITHOUT
4.17 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4.18 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
4.19 - * details.
4.20 - *
4.21 - * You should have received a copy of the GNU General Public License along with
4.22 - * this program. If not, see <http://www.gnu.org/licenses/>.
4.23 - *
4.24 - * As a special exception, AlliedModders LLC gives you permission to link the
4.25 - * code of this program (as well as its derivative works) to "Half-Life 2," the
4.26 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
4.27 - * by the Valve Corporation. You must obey the GNU General Public License in
4.28 - * all respects for all other code used. Additionally, AlliedModders LLC grants
4.29 - * this exception to all derivative works. AlliedModders LLC defines further
4.30 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
4.31 - * or <http://www.sourcemod.net/license.php>.
4.32 - *
4.33 - * Version: $Id$
4.34 - */
4.35 -
4.36 -#if defined _topmenus_included
4.37 - #endinput
4.38 -#endif
4.39 -#define _topmenus_included
4.40 -
4.41 -#include <menus>
4.42 -
4.43 -/**
4.44 - * Actions a top menu will take on an object.
4.45 - */
4.46 -enum TopMenuAction
4.47 -{
4.48 - /**
4.49 - * An option is being drawn for a menu (or for sorting purposes).
4.50 - *
4.51 - * INPUT : TopMenu Handle, object ID, client index.
4.52 - * OUTPUT: Buffer for rendering, maxlength of buffer.
4.53 - */
4.54 - TopMenuAction_DisplayOption = 0,
4.55 -
4.56 - /**
4.57 - * The title of a menu is being drawn for a given object.
4.58 - *
4.59 - * Note: The Object ID will be INVALID_TOPMENUOBJECT if drawing the
4.60 - * root title. Otherwise, the Object ID is a category.
4.61 - *
4.62 - * INPUT : TopMenu Handle, object ID, client index.
4.63 - * OUTPUT: Buffer for rendering, maxlength of buffer.
4.64 - */
4.65 - TopMenuAction_DisplayTitle = 1,
4.66 -
4.67 - /**
4.68 - * A menu option has been selected.
4.69 - *
4.70 - * The Object ID will always be an item (not a category).
4.71 - *
4.72 - * INPUT : TopMenu Handle, object ID, client index.
4.73 - */
4.74 - TopMenuAction_SelectOption = 2,
4.75 -
4.76 - /**
4.77 - * A menu option is being drawn and its flags can be overridden.
4.78 - *
4.79 - * INPUT : TopMenu Handle, object ID, client index.
4.80 - * OUTPUT: The first byte of the 'buffer' string should be set
4.81 - * to the desired flags. By default, it will contain
4.82 - * ITEMDRAW_DEFAULT.
4.83 - */
4.84 - TopMenuAction_DrawOption = 3,
4.85 -
4.86 - /**
4.87 - * Called when an object is being removed from the menu.
4.88 - * This can be used to clean up data stored in the info string.
4.89 - *
4.90 - * INPUT : TopMenu Handle, object ID.
4.91 - */
4.92 - TopMenuAction_RemoveObject = 4,
4.93 -};
4.94 -
4.95 -/**
4.96 - * Top menu object types.
4.97 - */
4.98 -enum TopMenuObjectType
4.99 -{
4.100 - TopMenuObject_Category = 0, /**< Category (sub-menu branching from root) */
4.101 - TopMenuObject_Item = 1 /**< Item on a sub-menu */
4.102 -};
4.103 -
4.104 -/**
4.105 - * Top menu starting positions for display.
4.106 - */
4.107 -enum TopMenuPosition
4.108 -{
4.109 - TopMenuPosition_Start = 0, /**< Start/root of the menu */
4.110 - TopMenuPosition_LastRoot = 1, /**< Last position in the root menu */
4.111 - TopMenuPosition_LastCategory = 3, /**< Last position in their last category */
4.112 -};
4.113 -
4.114 -/**
4.115 - * Top menu object tag for type checking.
4.116 - */
4.117 -enum TopMenuObject
4.118 -{
4.119 - INVALID_TOPMENUOBJECT = 0,
4.120 -}
4.121 -
4.122 -/**
4.123 - * TopMenu callback prototype.
4.124 - *
4.125 - * @param topmenu Handle to the TopMenu.
4.126 - * @param action TopMenuAction being performed.
4.127 - * @param object_id The object ID (if used).
4.128 - * @param param Extra parameter (if used).
4.129 - * @param buffer Output buffer (if used).
4.130 - * @param maxlength Output buffer (if used).
4.131 - * @noreturn
4.132 - */
4.133 -functag TopMenuHandler public(Handle:topmenu,
4.134 - TopMenuAction:action,
4.135 - TopMenuObject:object_id,
4.136 - param,
4.137 - String:buffer[],
4.138 - maxlength);
4.139 -
4.140 -/**
4.141 - * Creates a TopMenu.
4.142 - *
4.143 - * @param handler Handler to use for drawing the root title.
4.144 - * @return A new TopMenu Handle, or INVALID_HANDLE on failure.
4.145 - */
4.146 -native Handle:CreateTopMenu(TopMenuHandler:handler);
4.147 -
4.148 -/**
4.149 - * Re-sorts the items in a TopMenu via a configuration file.
4.150 - *
4.151 - * The format of the configuration file should be a Valve Key-Values
4.152 - * formatted file that SourceMod can parse. There should be one root
4.153 - * section, and one sub-section for each category. Each sub-section's
4.154 - * name should match the category name.
4.155 - *
4.156 - * Each sub-section may only contain key/value pairs in the form of:
4.157 - * key: "item"
4.158 - * value: Name of the item as passed to AddToTopMenu().
4.159 - *
4.160 - * The TopMenu will draw items in the order declared in the configuration
4.161 - * file. If items do not appear in the configuration file, they are sorted
4.162 - * per-player based on how the handler function renders for that player.
4.163 - * These items appear after the configuration sorted items.
4.164 - *
4.165 - * @param topmenu TopMenu Handle.
4.166 - * @param file File path.
4.167 - * @param error Error buffer.
4.168 - * @param maxlength Maximum size of the error buffer.
4.169 - * Error buffer will be filled with a
4.170 - * zero-terminated string if false is
4.171 - * returned.
4.172 - * @return True on success, false on failure.
4.173 - * @error Invalid TopMenu Handle.
4.174 - */
4.175 -native bool:LoadTopMenuConfig(Handle:topmenu, const String:file[], String:error[], maxlength);
4.176 -
4.177 -/**
4.178 - * Adds an object to a TopMenu.
4.179 - *
4.180 - * @param topmenu TopMenu Handle.
4.181 - * @param name Object name (MUST be unique).
4.182 - * @param type Object type.
4.183 - * @param handler Handler for object.
4.184 - * @param cmdname Command name (for access overrides).
4.185 - * @param flags Default access flags.
4.186 - * @param parent Parent object ID, or INVALID_TOPMENUOBJECT for none.
4.187 - * Items must have a category parent.
4.188 - * Categories must not have a parent.
4.189 - * @param info_string Arbitrary storage (max 255 bytes).
4.190 - * @return A new TopMenuObject ID, or INVALID_TOPMENUOBJECT on
4.191 - * failure.
4.192 - * @error Invalid TopMenu Handle.
4.193 - */
4.194 -native TopMenuObject:AddToTopMenu(Handle:topmenu,
4.195 - const String:name[],
4.196 - TopMenuObjectType:type,
4.197 - TopMenuHandler:handler,
4.198 - TopMenuObject:parent,
4.199 - const String:cmdname[]="",
4.200 - flags=0,
4.201 - const String:info_string[]="");
4.202 -
4.203 -/**
4.204 - * Retrieves the info string of a top menu item.
4.205 - *
4.206 - * @param topmenu TopMenu Handle.
4.207 - * @param object TopMenuObject ID.
4.208 - * @param buffer Buffer to store info string.
4.209 - * @param maxlength Maximum size of info string.
4.210 - * @return Number of bytes written, not including the
4.211 - * null terminator.
4.212 - * @error Invalid TopMenu Handle or TopMenuObject ID.
4.213 - */
4.214 -native GetTopMenuInfoString(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
4.215 -
4.216 -/**
4.217 - * Removes an object from a TopMenu.
4.218 - *
4.219 - * Plugins' objects are automatically removed all TopMenus when the given
4.220 - * plugin unloads or pauses. In the case of unpausing, all items are restored.
4.221 - *
4.222 - * @param topmenu TopMenu Handle.
4.223 - * @param object TopMenuObject ID.
4.224 - * @noreturn
4.225 - * @error Invalid TopMenu Handle.
4.226 - */
4.227 -native RemoveFromTopMenu(Handle:topmenu, TopMenuObject:object);
4.228 -
4.229 -/**
4.230 - * Displays a TopMenu to a client.
4.231 - *
4.232 - * @param topmenu TopMenu Handle.
4.233 - * @param client Client index.
4.234 - * @param position Position to display from.
4.235 - * @return True on success, false on failure.
4.236 - * @error Invalid TopMenu Handle or client not in game.
4.237 - */
4.238 -native bool:DisplayTopMenu(Handle:topmenu, client, TopMenuPosition:position);
4.239 -
4.240 -/**
4.241 - * Finds a category's object ID in a TopMenu.
4.242 - *
4.243 - * @param topmenu TopMenu Handle.
4.244 - * @param name Object's unique name.
4.245 - * @return TopMenuObject ID on success, or
4.246 - * INVALID_TOPMENUOBJECT on failure.
4.247 - * @error Invalid TopMenu Handle.
4.248 - */
4.249 -native TopMenuObject:FindTopMenuCategory(Handle:topmenu, const String:name[]);
4.250 -
4.251 -/**
4.252 - * Do not edit below this line!
4.253 - */
4.254 -public Extension:__ext_topmenus =
4.255 -{
4.256 - name = "TopMenus",
4.257 - file = "topmenus.ext",
4.258 -#if defined AUTOLOAD_EXTENSIONS
4.259 - autoload = 1,
4.260 -#else
4.261 - autoload = 0,
4.262 -#endif
4.263 -#if defined REQUIRE_EXTENSIONS
4.264 - required = 1,
4.265 -#else
4.266 - required = 0,
4.267 -#endif
4.268 -};
4.269 -
4.270 -#if !defined REQUIRE_EXTENSIONS
4.271 -public __ext_topmenus_SetNTVOptional()
4.272 -{
4.273 - MarkNativeAsOptional("CreateTopMenu");
4.274 - MarkNativeAsOptional("LoadTopMenuConfig");
4.275 - MarkNativeAsOptional("AddToTopMenu");
4.276 - MarkNativeAsOptional("RemoveFromTopMenu");
4.277 - MarkNativeAsOptional("DisplayTopMenu");
4.278 - MarkNativeAsOptional("FindTopMenuCategory");
4.279 -}
4.280 -#endif
4.281 +/**
4.282 + * vim: set ts=4 :
4.283 + * =============================================================================
4.284 + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
4.285 + * =============================================================================
4.286 + *
4.287 + * This file is part of the SourceMod/SourcePawn SDK.
4.288 + *
4.289 + * This program is free software; you can redistribute it and/or modify it under
4.290 + * the terms of the GNU General Public License, version 3.0, as published by the
4.291 + * Free Software Foundation.
4.292 + *
4.293 + * This program is distributed in the hope that it will be useful, but WITHOUT
4.294 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4.295 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
4.296 + * details.
4.297 + *
4.298 + * You should have received a copy of the GNU General Public License along with
4.299 + * this program. If not, see <http://www.gnu.org/licenses/>.
4.300 + *
4.301 + * As a special exception, AlliedModders LLC gives you permission to link the
4.302 + * code of this program (as well as its derivative works) to "Half-Life 2," the
4.303 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
4.304 + * by the Valve Corporation. You must obey the GNU General Public License in
4.305 + * all respects for all other code used. Additionally, AlliedModders LLC grants
4.306 + * this exception to all derivative works. AlliedModders LLC defines further
4.307 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
4.308 + * or <http://www.sourcemod.net/license.php>.
4.309 + *
4.310 + * Version: $Id$
4.311 + */
4.312 +
4.313 +#if defined _topmenus_included
4.314 + #endinput
4.315 +#endif
4.316 +#define _topmenus_included
4.317 +
4.318 +#include <menus>
4.319 +
4.320 +/**
4.321 + * Actions a top menu will take on an object.
4.322 + */
4.323 +enum TopMenuAction
4.324 +{
4.325 + /**
4.326 + * An option is being drawn for a menu (or for sorting purposes).
4.327 + *
4.328 + * INPUT : TopMenu Handle, object ID, client index.
4.329 + * OUTPUT: Buffer for rendering, maxlength of buffer.
4.330 + */
4.331 + TopMenuAction_DisplayOption = 0,
4.332 +
4.333 + /**
4.334 + * The title of a menu is being drawn for a given object.
4.335 + *
4.336 + * Note: The Object ID will be INVALID_TOPMENUOBJECT if drawing the
4.337 + * root title. Otherwise, the Object ID is a category.
4.338 + *
4.339 + * INPUT : TopMenu Handle, object ID, client index.
4.340 + * OUTPUT: Buffer for rendering, maxlength of buffer.
4.341 + */
4.342 + TopMenuAction_DisplayTitle = 1,
4.343 +
4.344 + /**
4.345 + * A menu option has been selected.
4.346 + *
4.347 + * The Object ID will always be an item (not a category).
4.348 + *
4.349 + * INPUT : TopMenu Handle, object ID, client index.
4.350 + */
4.351 + TopMenuAction_SelectOption = 2,
4.352 +
4.353 + /**
4.354 + * A menu option is being drawn and its flags can be overridden.
4.355 + *
4.356 + * INPUT : TopMenu Handle, object ID, client index.
4.357 + * OUTPUT: The first byte of the 'buffer' string should be set
4.358 + * to the desired flags. By default, it will contain
4.359 + * ITEMDRAW_DEFAULT.
4.360 + */
4.361 + TopMenuAction_DrawOption = 3,
4.362 +
4.363 + /**
4.364 + * Called when an object is being removed from the menu.
4.365 + * This can be used to clean up data stored in the info string.
4.366 + *
4.367 + * INPUT : TopMenu Handle, object ID.
4.368 + */
4.369 + TopMenuAction_RemoveObject = 4,
4.370 +};
4.371 +
4.372 +/**
4.373 + * Top menu object types.
4.374 + */
4.375 +enum TopMenuObjectType
4.376 +{
4.377 + TopMenuObject_Category = 0, /**< Category (sub-menu branching from root) */
4.378 + TopMenuObject_Item = 1 /**< Item on a sub-menu */
4.379 +};
4.380 +
4.381 +/**
4.382 + * Top menu starting positions for display.
4.383 + */
4.384 +enum TopMenuPosition
4.385 +{
4.386 + TopMenuPosition_Start = 0, /**< Start/root of the menu */
4.387 + TopMenuPosition_LastRoot = 1, /**< Last position in the root menu */
4.388 + TopMenuPosition_LastCategory = 3, /**< Last position in their last category */
4.389 +};
4.390 +
4.391 +/**
4.392 + * Top menu object tag for type checking.
4.393 + */
4.394 +enum TopMenuObject
4.395 +{
4.396 + INVALID_TOPMENUOBJECT = 0,
4.397 +}
4.398 +
4.399 +/**
4.400 + * TopMenu callback prototype.
4.401 + *
4.402 + * @param topmenu Handle to the TopMenu.
4.403 + * @param action TopMenuAction being performed.
4.404 + * @param object_id The object ID (if used).
4.405 + * @param param Extra parameter (if used).
4.406 + * @param buffer Output buffer (if used).
4.407 + * @param maxlength Output buffer (if used).
4.408 + * @noreturn
4.409 + */
4.410 +functag TopMenuHandler public(Handle:topmenu,
4.411 + TopMenuAction:action,
4.412 + TopMenuObject:object_id,
4.413 + param,
4.414 + String:buffer[],
4.415 + maxlength);
4.416 +
4.417 +/**
4.418 + * Creates a TopMenu.
4.419 + *
4.420 + * @param handler Handler to use for drawing the root title.
4.421 + * @return A new TopMenu Handle, or INVALID_HANDLE on failure.
4.422 + */
4.423 +native Handle:CreateTopMenu(TopMenuHandler:handler);
4.424 +
4.425 +/**
4.426 + * Re-sorts the items in a TopMenu via a configuration file.
4.427 + *
4.428 + * The format of the configuration file should be a Valve Key-Values
4.429 + * formatted file that SourceMod can parse. There should be one root
4.430 + * section, and one sub-section for each category. Each sub-section's
4.431 + * name should match the category name.
4.432 + *
4.433 + * Each sub-section may only contain key/value pairs in the form of:
4.434 + * key: "item"
4.435 + * value: Name of the item as passed to AddToTopMenu().
4.436 + *
4.437 + * The TopMenu will draw items in the order declared in the configuration
4.438 + * file. If items do not appear in the configuration file, they are sorted
4.439 + * per-player based on how the handler function renders for that player.
4.440 + * These items appear after the configuration sorted items.
4.441 + *
4.442 + * @param topmenu TopMenu Handle.
4.443 + * @param file File path.
4.444 + * @param error Error buffer.
4.445 + * @param maxlength Maximum size of the error buffer.
4.446 + * Error buffer will be filled with a
4.447 + * zero-terminated string if false is
4.448 + * returned.
4.449 + * @return True on success, false on failure.
4.450 + * @error Invalid TopMenu Handle.
4.451 + */
4.452 +native bool:LoadTopMenuConfig(Handle:topmenu, const String:file[], String:error[], maxlength);
4.453 +
4.454 +/**
4.455 + * Adds an object to a TopMenu.
4.456 + *
4.457 + * @param topmenu TopMenu Handle.
4.458 + * @param name Object name (MUST be unique).
4.459 + * @param type Object type.
4.460 + * @param handler Handler for object.
4.461 + * @param cmdname Command name (for access overrides).
4.462 + * @param flags Default access flags.
4.463 + * @param parent Parent object ID, or INVALID_TOPMENUOBJECT for none.
4.464 + * Items must have a category parent.
4.465 + * Categories must not have a parent.
4.466 + * @param info_string Arbitrary storage (max 255 bytes).
4.467 + * @return A new TopMenuObject ID, or INVALID_TOPMENUOBJECT on
4.468 + * failure.
4.469 + * @error Invalid TopMenu Handle.
4.470 + */
4.471 +native TopMenuObject:AddToTopMenu(Handle:topmenu,
4.472 + const String:name[],
4.473 + TopMenuObjectType:type,
4.474 + TopMenuHandler:handler,
4.475 + TopMenuObject:parent,
4.476 + const String:cmdname[]="",
4.477 + flags=0,
4.478 + const String:info_string[]="");
4.479 +
4.480 +/**
4.481 + * Retrieves the info string of a top menu item.
4.482 + *
4.483 + * @param topmenu TopMenu Handle.
4.484 + * @param object TopMenuObject ID.
4.485 + * @param buffer Buffer to store info string.
4.486 + * @param maxlength Maximum size of info string.
4.487 + * @return Number of bytes written, not including the
4.488 + * null terminator.
4.489 + * @error Invalid TopMenu Handle or TopMenuObject ID.
4.490 + */
4.491 +native GetTopMenuInfoString(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
4.492 +
4.493 +/**
4.494 + * Retrieves the name string of a top menu item.
4.495 + *
4.496 + * @param topmenu TopMenu Handle.
4.497 + * @param object TopMenuObject ID.
4.498 + * @param buffer Buffer to store info string.
4.499 + * @param maxlength Maximum size of info string.
4.500 + * @return Number of bytes written, not including the
4.501 + * null terminator.
4.502 + * @error Invalid TopMenu Handle or TopMenuObject ID.
4.503 + */
4.504 +native GetTopMenuName(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
4.505 +
4.506 +/**
4.507 + * Removes an object from a TopMenu.
4.508 + *
4.509 + * Plugins' objects are automatically removed all TopMenus when the given
4.510 + * plugin unloads or pauses. In the case of unpausing, all items are restored.
4.511 + *
4.512 + * @param topmenu TopMenu Handle.
4.513 + * @param object TopMenuObject ID.
4.514 + * @noreturn
4.515 + * @error Invalid TopMenu Handle.
4.516 + */
4.517 +native RemoveFromTopMenu(Handle:topmenu, TopMenuObject:object);
4.518 +
4.519 +/**
4.520 + * Displays a TopMenu to a client.
4.521 + *
4.522 + * @param topmenu TopMenu Handle.
4.523 + * @param client Client index.
4.524 + * @param position Position to display from.
4.525 + * @return True on success, false on failure.
4.526 + * @error Invalid TopMenu Handle or client not in game.
4.527 + */
4.528 +native bool:DisplayTopMenu(Handle:topmenu, client, TopMenuPosition:position);
4.529 +
4.530 +/**
4.531 + * Finds a category's object ID in a TopMenu.
4.532 + *
4.533 + * @param topmenu TopMenu Handle.
4.534 + * @param name Object's unique name.
4.535 + * @return TopMenuObject ID on success, or
4.536 + * INVALID_TOPMENUOBJECT on failure.
4.537 + * @error Invalid TopMenu Handle.
4.538 + */
4.539 +native TopMenuObject:FindTopMenuCategory(Handle:topmenu, const String:name[]);
4.540 +
4.541 +/**
4.542 + * Do not edit below this line!
4.543 + */
4.544 +public Extension:__ext_topmenus =
4.545 +{
4.546 + name = "TopMenus",
4.547 + file = "topmenus.ext",
4.548 +#if defined AUTOLOAD_EXTENSIONS
4.549 + autoload = 1,
4.550 +#else
4.551 + autoload = 0,
4.552 +#endif
4.553 +#if defined REQUIRE_EXTENSIONS
4.554 + required = 1,
4.555 +#else
4.556 + required = 0,
4.557 +#endif
4.558 +};
4.559 +
4.560 +#if !defined REQUIRE_EXTENSIONS
4.561 +public __ext_topmenus_SetNTVOptional()
4.562 +{
4.563 + MarkNativeAsOptional("CreateTopMenu");
4.564 + MarkNativeAsOptional("LoadTopMenuConfig");
4.565 + MarkNativeAsOptional("AddToTopMenu");
4.566 + MarkNativeAsOptional("RemoveFromTopMenu");
4.567 + MarkNativeAsOptional("DisplayTopMenu");
4.568 + MarkNativeAsOptional("FindTopMenuCategory");
4.569 +}
4.570 +#endif