/*Menu.c*/ /* #if defined(macintosh) #include <types.h> #else #include <sys/types.h> #endif #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> */ using namespace std; #include <iostream> #include "merc.h" #include "interp.h" #include "recycle.h" /*Stuff: menu_list is the prototype list of menus */ MENU_DATA *menu_free; MENU_DATA *menu_list; OPTION_DATA *option_free; int top_menu; //local for now OPTION_DATA *get_option(MENU_DATA *menu, int whichopt); /************************************************************************* Steps to making a new menu: MENU_DATA *menu = new_menu(); or MENU_DATA *menu = new_menu("name"); Parameters: int id --unused int menu_flags --unused for now DO_FUN *fun_from --What function called the menu DO_FUN *do_fun --Menus can call functions, but options usually do char *text --General text--typically used for meat of menu char *opening --If new menu is displayed for char, display/use this message (useful for acts) char *closing --If menu is closed/quit from, display/use this message (useful for acts) Add options: OPTION_DATA *option = new_option(); Optional parameters: int key -- Almost required, when presented with a menu, this is the usually the value which is compared against your reply. char *args[5] -- Use depending on menu. DO_FUN *fun_call -- A function to be called when the option is selected *************************************************************************/ //All the code for exec_menu is tentative. //needs to be reworked, below is just, um, nonoperative, but OK. //sit down with some paper and figure it out. //since many menus are actually made in-code, I don't see how many //menus will be called. There's going to have to be some 'hardcoded' //menus. //Thinking update: Menus 'in code' will have to use fun calls, not menus. void exec_menu (CHAR_DATA *ch, MENU_DATA *menu, int foo, char *argument) { OPTION_DATA *option; bool mRemove = true; if (IS_NPC(ch)) return; //reminder: foo is 1-biased, code elsewhere treats it as 1-biased and converts to 0-biased ch_printf(ch, "menu.c %d: Exec_menu called\n\r", __LINE__); ch_printf(ch, "{GFoo: %d {CArgument: %s{x\n\r", foo, argument); //find out what to do: foo will be negative sometimes; if that's the case, use //the argument to snatch a special command. if (foo <= 0) { if (!is_number(argument)) { switch (toupper(*argument)) { case '?': show_menu(ch, menu, TRUE); break; case 'X': case 'Q': //Later, when menus are events, just set timer to 1 or 0, that //way the event that called the menu set (i.e. a conversation //event or a skill edit event) can have a destroy funct //save the table, edit the player's char, etc. //menu_from_char(ch, menu); //have to be careful with the below :) JH 6/10/2004 11:36PM if (IS_SET(menu->menu_flags, MENU_NOEXIT)) { ch_printf(ch, "Sorry, you cannot exit out of this menu. A response is required.\n\r"); return; } //fixme bug (unavoidable), noticed 6/17/2004 12:17AM: // //you can spam 'x' like 4 times before the event actually boots you. //is this fixable without being hackish? //is it worth fixing? //force the menu to non-phoenix..kind of sloppy? JH 6/10/2004 11:33PM //ch_printf(ch, "Exiting '%s'.\n\r", menu->name); REMOVE_BIT(menu->menu_flags, MENU_PHOENIX); menu->ev->delay = 0; //potentially crashy 6/10/2004 11:19PM not anymore? break; default: ch_printf(ch, "Invalid response. Try 'Q' to quit, '?' to see your options.\n\r"); } return; } else { foo = atoi(argument); } } if (menu->fun_call) do_function(ch, menu->fun_call, argument); else { if ((option = get_option(menu, foo)) == NULL) { //invalid choice code, don't free. Person just typed in shit. ch_printf(ch, "Invalid response. Try 'Q' to quit, '?' to see your options.\n\r"); mRemove = false; } else { if (option->fun_call) do_function(ch, option->fun_call, argument); else if (option->menu_call) { menu_to_char(ch, option->menu_call); ch->pcdata->menu_locked = option->menu_call; show_menu(ch, option->menu_call); } else { //nothing to do error mRemove = false; } } } if (mRemove) { if(IS_SET(menu->menu_flags, MENU_PHOENIX)) menu->ev->delay = 1; //give the phoenixed menu a little bit to fade. else menu->ev->delay = 0; } } OPTION_DATA *get_option(MENU_DATA *menu, int whichopt) { OPTION_DATA *option = NULL; if (whichopt < 0) return NULL; int i = 1; //input from ch is typically 1-biased for (option = menu->option; option; option = option->next) if (i++ == whichopt) break; return option; } /* stuff for recycling menus */ /*Overloaded new_menu, for when you passing in name of menu*/ MENU_DATA *new_menu (const char *argument) { static MENU_DATA menu_zero; MENU_DATA *menu; if (menu_free == NULL) menu = (MENU_DATA *) alloc_perm (sizeof (*menu)); else { menu = menu_free; menu_free = menu_free->next; } *menu = menu_zero; VALIDATE (menu); menu->name = str_dup(argument); /*paranoia*/ menu->fun_from = NULL; menu->fun_call = NULL; menu->text = NULL; menu->opening = NULL; menu->closing = NULL; return menu; } MENU_DATA *new_menu (void) { static MENU_DATA menu_zero; MENU_DATA *menu; if (menu_free == NULL) menu = (MENU_DATA *) alloc_perm (sizeof (*menu)); else { menu = menu_free; menu_free = menu_free->next; } *menu = menu_zero; VALIDATE (menu); /*paranoia*/ menu->name = NULL; menu->fun_from = NULL; menu->fun_call = NULL; menu->text = NULL; menu->opening = NULL; menu->closing = NULL; return menu; } void free_menu (MENU_DATA * menu) { OPTION_DATA *option, *option_next; if (!IS_VALID (menu)) return; free_string(menu->name); free_string(menu->text); free_string(menu->opening); free_string(menu->closing); menu->argi = 0; INVALIDATE (menu); for (option = menu->option; option; option = option_next) { option_next = option->next; free_option(option); } menu->next = menu_free; menu_free = menu; return; } void free_option (OPTION_DATA * option) { if (!IS_VALID (option)) return; int i; for (i = 0; i < 5; i++) free_string(option->args[i]); option->argi = 0; INVALIDATE (option); option->next = option_free; option_free = option; } OPTION_DATA *new_option (void) { static OPTION_DATA option_zero; OPTION_DATA *option; if (option_free == NULL) option = (OPTION_DATA *) alloc_perm (sizeof (*option)); else { option = option_free; option_free = option_free->next; } *option = option_zero; VALIDATE (option); /*paranoia*/ for (int i = 0; i < 5; i++) option->args[i] = NULL; option->fun_call = NULL; option->menu_call = NULL; return option; } void fiddlesticks() { // int cmd; MENU_DATA *menu; writelogf("Pointing! (fiddlesticks)"); menu = new_menu(); menu->name = str_dup("HC00_do_list"); menu->fun_call = do_buy; //wtf was I thinking below? /* for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++) { if (!str_cmp(cmd_table[cmd].name, "buy")) { writelogf("Pointing! (fiddlesticks)"); menu->fun_call = cmd_table[cmd].do_fun; } } */ menu_to_global(menu); return; } /*Adds a menu to the global list*/ void menu_to_global (MENU_DATA * menu) { menu->next = menu_list; menu_list = menu; return; } /*Removes a menu from the global list*/ void menu_from_global (MENU_DATA *menu) { if (menu == menu_list) { menu_list = menu->next; } else { MENU_DATA *prev; for (prev = menu_list; prev != NULL; prev = prev->next) { if (prev->next == menu) { prev->next = menu->next; break; } } if (prev == NULL) { bug ("menu_delete: Menu not found.", 0); return; } } free_menu(menu); /*Recycle*/ return; } /*Give a menu to a char*/ void menu_to_char (CHAR_DATA *ch, MENU_DATA * menu) { if (IS_NPC(ch)) return; menu->next = ch->pcdata->menu; ch->pcdata->menu = menu; return; } /* Remove an menu from a char.*/ void menu_from_char (CHAR_DATA * ch, MENU_DATA * menu) { if (IS_NPC(ch)) return; if (ch->pcdata->menu == NULL) { bug ("Menu_remove: no menu.", 0); return; } if (menu == ch->pcdata->menu) { ch->pcdata->menu = menu->next; } else { MENU_DATA *prev; for (prev = ch->pcdata->menu; prev != NULL; prev = prev->next) { if (prev->next == menu) { prev->next = menu->next; break; } } if (prev == NULL) { bug ("Menu_from_char: cannot find menu.", 0); return; } } //unlock! if (menu == ch->pcdata->menu_locked) ch->pcdata->menu_locked = NULL; // free_menu (menu); return; } //HOPE this works............... void menu_ch_to_world (CHAR_DATA * ch, MENU_DATA * menu) { if (IS_NPC(ch)) return; //rewritten, inefficient JH 6/3/2004 5:04PM menu_to_global (menu); menu_from_char(ch, menu); /* if (ch->pcdata->menu == NULL) { bug ("Menu_remove: no menu.", 0); return; } if (menu == ch->pcdata->menu) { ch->pcdata->menu = menu->next; } else { MENU_DATA *prev; for (prev = ch->pcdata->menu; prev != NULL; prev = prev->next) { if (prev->next == menu) { prev->next = menu->next; break; } } if (prev == NULL) { bug ("Menu_remove: cannot find menu.", 0); return; } } */ } /* Remove an option from a menu*/ void option_from_menu (MENU_DATA * menu, OPTION_DATA *option) { if (menu->option == NULL) { bug ("Option_from_menu: no options.", 0); return; } if (option == menu->option) { menu->option = option->next; } else { OPTION_DATA *prev; for (prev = menu->option; prev != NULL; prev = prev->next) { if (prev->next == option) { prev->next = option->next; break; } } if (prev == NULL) { bug ("Option_from_menu: cannot find option.", 0); return; } } free_option (option); return; } // void append_option //Tack on an option to a menu. Similar to push_back() for the STL vector //class. //Key is optional. If specified, the option will be assigned said key. //Otherwise it'll be assigned a key based on its position. //(note: show_menu ignores keys at the moment) void append_option(MENU_DATA * menu, OPTION_DATA * option, int key) { OPTION_DATA *oLast; int key_last = 0; if (!menu->option) { menu->option = option; return; } for (oLast = menu->option; oLast; oLast = oLast->next, ++key_last) { if (oLast->next == NULL) { (key > 0) ? option->key = key : option->key = key_last + 1; oLast->next = option; oLast = menu->option; menu->option = oLast; break; } } return; } /*Utility commands - show menu to char, */ //unfinished, IMO (key stuff?) void show_menu(CHAR_DATA *ch, MENU_DATA *menu, bool ShowKey) { OPTION_DATA *option; BUFFER *output; char buf[MSL]; *buf = '\0'; int i = 0; output = new_buf(); if (!menu) { ch_printf(ch, "Uhoh! %s:%d, Menu is null.\n\r", __FILE__, __LINE__); return; } //if (IS_NPC(ch)) return; //If (menu->opening) for (option = menu->option; option; option = option->next) { sprintf(buf, "{G%2d. {x%s\n\r", ++i, option->args[0]); add_buf(output, buf); } page_to_char (buf_string (output), ch); free_buf (output); return; } //Utility: Find a menu from the list MENU_DATA *find_menu(const char *argument) { MENU_DATA *menu; for (menu = menu_list; menu; menu = menu->next) if (!str_cmp(argument, menu->name)) return menu; return NULL; } MENU_DATA *find_menu_char(CHAR_DATA *ch, const char *argument) { MENU_DATA *menu; if (IS_NPC(ch)) return NULL; for (menu = ch->pcdata->menu; menu; menu = menu->next) if (!str_cmp(argument, menu->name)) return menu; return NULL; } //Utility: New menu's name is okay? bool bad_menu_name (const char *argument) { MENU_DATA *menu; for (menu = menu_list; menu; menu = menu->next) if (!str_cmp(argument, menu->name)) return TRUE; return FALSE; } int create_menu() { return 0; } //Lock a menu onto a character. Having a menu locked to you is like being //in an OLC editor. bool menu_lock(CHAR_DATA *ch, MENU_DATA *menu) { if (IS_NPC(ch)) { free_menu(menu); return false; } if (ch->pcdata->menu_locked) { wiznet(create_string("DEBUG: menu_lock: ch '%s' has menu locked already", ch->name), NULL, NULL, WIZ_DEBUG, 0, MAX_LEVEL); ch_printf(ch, "{RError: cannot lock menu '%s' to you. {YPlease note immortal.{x\n\r", menu->name); bugf("%s:%d:menu_lock: Failed locking menu to '%s'. Freeing menu.", __FILE__, __LINE__, ch->name); free_menu(menu); return false; } ch->pcdata->menu_locked = menu; return true; }