/* Relay -- a tool to record and play Quake2 demos Copyright (C) 2000 Conor Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Conor Davis cedavis@planetquake.com */ #include #include #include "sv_local.h" #define MAX_MENU_ITEMS 128 static menuitem_t temp_items[MAX_MENU_ITEMS]; static int num_items; void Menu_Close(client_t *client, qboolean update) { menu_t *menu; int i; menu = client->curmenu; if (!menu) return; if (menu->Close) menu->Close(client, menu); // remove menu from list client->curmenu = menu->next; // free contents if (menu->items) { for (i = 0; i < menu->num; i++) { if (menu->items[i].text) Z_Free(menu->items[i].text); } Z_Free(menu->items); } Z_Free(menu); if (update) { if (client->curmenu) Menu_Display(client); else if (client->player != -1 && client->flags & RC_LAYOUT) { strcpy(client->layout, dm2in.players[client->player].layout); client->layout_modified = true; } else { client->ps.stats[STAT_LAYOUTS] &= ~1; } } } void Menu_CloseAll(client_t *client, qboolean update) { while (client->curmenu) Menu_Close(client, false); if (update) { if (client->player != -1 && client->flags & RC_LAYOUT) { strcpy(client->layout, dm2in.players[client->player].layout); client->layout_modified = true; } else { client->ps.stats[STAT_LAYOUTS] &= ~1; } } } int Menu_AddItem(const char *text, const char *fmt, ...) { menuitem_t *item; va_list argptr; item = &temp_items[num_items++]; memset(item, 0, sizeof(menuitem_t)); item->text = Z_Strdup(text); if (!fmt) return num_items - 1; va_start(argptr, fmt); while (*fmt) { switch(toupper(*fmt)) { case 'A': item->align = va_arg(argptr, int); break; case 'O': item->indent = va_arg(argptr, int); break; case 'F': item->Select = va_arg(argptr, void *); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': item->param[*fmt - '0'] = va_arg(argptr, int); break; default: printf("Menu_AddItem: unknown option '%c'\n", *fmt); } fmt++; } va_end(argptr); return num_items - 1; } void Menu_Display(client_t *client) { int y, i, num; menuitem_t *item; menu_t *menu; menu = client->curmenu; if (!menu) { printf("DisplayMenu: no menu for %s\n", client->netname); return; } client->layout_modified = true; client->layout[0] = 0; // title if (menu->title && menu->title[0]) strcatf(client->layout, sizeof(client->layout), "yv 32 xv %d string2 \"%s\" ", 144 - strlen(menu->title)*4, menu->title); if (menu->cur != -1) { if (menu->top > menu->cur) menu->top = menu->cur; if (menu->top < menu->cur - 9) menu->top = menu->cur - 9; if (menu->top > menu->num - 10) menu->top = menu->num - 10; if (menu->top < 0) menu->top = 0; } // last chance to stop messups if (menu->cur != -1) { if (menu->cur >= menu->num || !menu->items[menu->cur].Select) { printf("Menu had invalid cur: %d\n", menu->cur); menu->cur = -1; } } if (menu->top > 0) strcatf(client->layout, sizeof(client->layout), "yv 48 xv 0 string2 \"(Up)\" "); num = 0; y = 56; for (i = 0; i < 10; i++) { if (menu->top + i >= menu->num) break; item = &menu->items[menu->top + i]; if (item->Select) { num++; if (menu->cur == -1) menu->cur = menu->top + i; if (menu->cur == menu->top + i) strcatf(client->layout, sizeof(client->layout), "yv %d xv 0 string2 \"%d %s\" ", y, num % 10, item->text); else strcatf(client->layout, sizeof(client->layout), "yv %d xv 0 string \"%d %s\" ", y, num % 10, item->text); } else strcatf(client->layout, sizeof(client->layout), "yv %d xv 16 string \"%s\" ", y, item->text); y += 8; } if (menu->top + 10 < menu->num) strcatf(client->layout, sizeof(client->layout), "yv 136 xv 0 string2 \"(Down)\" "); strcatf(client->layout, sizeof(client->layout), "yv 152 xv 0 string2 \"[ ] move cursor up/down\" "); strcatf(client->layout, sizeof(client->layout), "yv 160 string2 \"Enter to select; ' to close\" "); strcatf(client->layout, sizeof(client->layout), "yv 168 string2 \"F1 for help\" "); } void Menu_Update(client_t *client, int id) { menu_t *menu; for (menu = client->curmenu; menu; menu = menu->next) { if (menu->id == id) { menu->Show(client, menu); // in case the menu was closed if (!client->curmenu) return; if (menu == client->curmenu) Menu_Display(client); } } } void Menu_UpdateAll(int id) { client_t *client; int i; for (i = 0, client = server.clients; i < server.maxclients; i++, client++) { if (client->status != CL_CONNECTED) continue; Menu_Update(client, id); } } void Menu_Prev(client_t *client) { menu_t *menu; int i; menu = client->curmenu; if (!menu) return; for (i = menu->cur - 1; i >= 0; i--) { if (menu->items[i].Select) { menu->cur = i; Menu_Display(client); return; } } for (i = menu->num - 1; i > menu->cur; i--) { if (menu->items[i].Select) { menu->cur = i; Menu_Display(client); return; } } } void Menu_Next(client_t *client) { menu_t *menu; int i; menu = client->curmenu; if (!menu) return; for (i = menu->cur + 1; i < menu->num; i++) { if (menu->items[i].Select) { menu->cur = i; Menu_Display(client); return; } } for (i = 0; i < menu->cur; i++) { if (menu->items[i].Select) { menu->cur = i; Menu_Display(client); return; } } } void Menu_Select(client_t *client, int key) { menu_t *menu; menu = client->curmenu; if (!menu) return; if (menu->cur < 0 || menu->cur >= menu->num) return; if (!menu->items[menu->cur].Select) return; menu->items[menu->cur].Select(client, &menu->items[menu->cur], key); } // Use SelectOpenMenu as an item's Select function to open a menu when it is selected // param[0] = Show function // param[1] = format string passed to Menu_Open() // param[2..7] = arguments to Menu_Open() void Menu_SelectOpen(client_t *client, menuitem_t *item, int key) { if (!item->param[0]) { printf("Menu_SelectOpen: param[0] is NULL for item \"%s\"\n", item->text); return; } Menu_Open(client, (void (*)(client_t *, struct menu_s *))item->param[0], (item->param[1] ? (char *)item->param[1] : ""), item->param[2], item->param[3], item->param[4], item->param[5], item->param[6], item->param[7]); } void Menu_Start(menu_t *menu) { num_items = 0; if (menu->items) { Z_Free(menu->items); menu->items = NULL; } } void Menu_Finish(menu_t *menu) { menu->items = Z_Malloc(num_items*sizeof(menuitem_t)); memcpy(menu->items, temp_items, num_items*sizeof(menuitem_t)); menu->num = num_items; } void Menu_Open(client_t *client, void (*Show)(client_t *, struct menu_s *), const char *fmt, ...) { menu_t *menu; va_list argptr; if (!fmt) return; menu = Z_Malloc(sizeof(menu_t)); memset(menu, 0, sizeof(menu_t)); menu->cur = -1; va_start(argptr, fmt); while (*fmt) { switch(toupper(*fmt)) { case 'T': menu->title = Z_Strdup(va_arg(argptr, char *)); break; case 'I': menu->id = va_arg(argptr, int); break; case 'C': menu->Close = va_arg(argptr, void *); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': menu->param[*fmt - '0'] = va_arg(argptr, int); break; default: printf("Menu_Open: unknown option '%c'\n", *fmt); } fmt++; } va_end(argptr); menu->next = client->curmenu; client->curmenu = menu; menu->Show = Show; if (!menu->Show) return; menu->Show(client, menu); Menu_Display(client); }