/* * This file handles command interpreting */ #include #include /* include main header file */ #include "mud.h" #include "utils.h" #include "character.h" #include "socket.h" #include "room.h" #include "commands.h" #include "action.h" #include "hooks.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "scripts/scripts.h" //***************************************************************************** // optional modules //***************************************************************************** #ifdef MODULE_FACULTY #include "faculty/faculty.h" #endif #ifdef MODULE_ALIAS #include "alias/alias.h" #endif #ifdef MODULE_SOCIALS #include "socials/socials.h" #endif //***************************************************************************** // local variables and functions //***************************************************************************** NEAR_MAP *cmd_table = NULL; void init_commands() { cmd_table = newNearMap(); //*************************************************************************** // This is for core functions ONLY! If you have a module that adds new // functions to the MUD, they should be added in the init_xxx() function // associated with your module. //*************************************************************************** add_cmd("wroc", NULL, cmd_back, "player", FALSE); add_cmd("komendy", NULL, cmd_commands, "player", FALSE); add_cmd("groupcmds", NULL, cmd_groupcmds, "admin", FALSE); add_cmd("patrz", "p", cmd_look, "player", FALSE); add_cmd("wiecej", NULL, cmd_more, "player", FALSE); add_cmd_check("patrz", chk_conscious); } bool cmd_exists(const char *cmd) { return nearMapKeyExists(cmd_table, cmd); } CMD_DATA *remove_cmd(const char *cmd) { return nearMapRemove(cmd_table, cmd); } void add_cmd(const char *cmd, const char *sort_by, COMMAND(func), const char *user_group, bool interrupts) { // do we already have an existing command we're just updating? CMD_DATA *data = remove_cmd(cmd); // is it something that needs to be updated or erased? if(data != NULL && !cmdHasFunc(data)) cmdUpdate(data, func, user_group, interrupts); else { if(data != NULL) deleteCmd(data); data = newCmd(cmd, func, user_group, interrupts); } // add in the new command nearMapPut(cmd_table, cmd, sort_by, data); } void add_py_cmd(const char *cmd, const char *sort_by, void *pyfunc, const char *user_group, bool interrupts) { // do we already have an existing command we're just updating? CMD_DATA *data = remove_cmd(cmd); // is this something that needs to be updated or erased? if(data != NULL && !cmdHasFunc(data)) cmdPyUpdate(data, pyfunc, user_group, interrupts); else { if(data != NULL) deleteCmd(data); data = newPyCmd(cmd, pyfunc, user_group, interrupts); } // add in the new command nearMapPut(cmd_table, cmd, sort_by, data); } void add_cmd_check(const char *cmd, CMD_CHK(func)) { CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE); // make a temp container for just the checks if(data == NULL) { data = newCmd(cmd, NULL, "", FALSE); nearMapPut(cmd_table, cmd, NULL, data); } cmdAddCheck(data, func); } void add_py_cmd_check(const char *cmd, void *pyfunc) { CMD_DATA *data = nearMapGet(cmd_table, cmd, FALSE); // make a temp container just for the checks if(data == NULL) { data = newCmd(cmd, NULL, "", FALSE); nearMapPut(cmd_table, cmd, NULL, data); } cmdAddPyCheck(data, pyfunc); } // show the character all of the commands in the specified group(s). void show_commands(CHAR_DATA *ch, const char *user_groups) { BUFFER *buf = newBuffer(MAX_BUFFER); NEAR_ITERATOR *near_i = newNearIterator(cmd_table); const char *abbrev = NULL; CMD_DATA *cmd = NULL; int col = 0; // go over all of our buckets ITERATE_NEARMAP(abbrev, cmd, near_i) { if(cmdHasFunc(cmd) && is_keyword(user_groups, cmdGetUserGroup(cmd), FALSE)) { #ifdef MODULE_SOCIALS if (get_social(cmdGetName(cmd)) != NULL) { continue; } #endif bprintf(buf, "%-13.13s", cmdGetName(cmd)); if (!(++col % 6)) { bufferCat(buf, "\r\n"); } } } deleteNearIterator(near_i); // do room commands as well if(roomHasCmds(charGetRoom(ch))) { near_i = newNearIterator(roomGetCmdTable(charGetRoom(ch))); abbrev = NULL; cmd = NULL; bufferCat(buf, "{c"); ITERATE_NEARMAP(abbrev, cmd, near_i) { if(cmdHasFunc(cmd) && is_keyword(user_groups,cmdGetUserGroup(cmd),FALSE)){ bprintf(buf, "%-13.13s", cmdGetName(cmd)); if (!(++col % 6)) bufferCat(buf, "\r\n"); } } deleteNearIterator(near_i); bufferCat(buf, "{n"); } // tag on our last newline if neccessary, and show commands if (col % 6) bprintf(buf, "\r\n"); text_to_char(ch, bufferString(buf)); deleteBuffer(buf); } // // return whether the command is usable by the character bool is_usable_cmd(CHAR_DATA *ch, CMD_DATA *cmd) { // this is a check, not a command if(*cmdGetUserGroup(cmd) == '\0') return FALSE; return is_keyword(bitvectorGetBits(charGetUserGroups(ch)), cmdGetUserGroup(cmd), FALSE); } // // checks are commands without a specific user group, added to finer-grain // command tables (e.g., characters, to rooms, to zones, to the world). This // is for e.g., allowing for the addition of checks before leaving a room // (maybe you have to move a boulder out of the way first, etc...) CMD_DATA *find_check(CHAR_DATA *ch, NEAR_MAP *table, const char *name) { CMD_DATA *check = nearMapGet(table, name, FALSE); /* if(check == NULL) || *cmdGetUserGroup(check) != '\0') return NULL; */ return check; } // // tries to find the relevant command, usable by the character CMD_DATA *find_cmd(CHAR_DATA *ch, NEAR_MAP *table, const char *name, bool abbrev_ok) { if(abbrev_ok == FALSE) { CMD_DATA *cmd = nearMapGet(table, name, FALSE); // no command exists if(cmd == NULL || !is_usable_cmd(ch, cmd)) return NULL; return cmd; } else { // try to look up the possible commands LIST *cmdname_list = nearMapGetAllMatches(table, name); CMD_DATA *cmd = NULL; if(cmdname_list != NULL) { LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list); const char *cmdname = NULL; ITERATE_LIST(cmdname, cmdname_i) { cmd = nearMapGet(table, cmdname, FALSE); if(is_usable_cmd(ch, cmd)) break; else cmd = NULL; } deleteListIterator(cmdname_i); deleteListWith(cmdname_list, free); } return cmd; } } // tries to pull a usable command from the near-table and use it. Returns // TRUE if a usable command was found (even if it failed) and false otherwise. bool try_use_cmd_table(CHAR_DATA *ch, NEAR_MAP *table, const char *command, char *arg, bool abbrev_ok) { if(abbrev_ok == FALSE) { CMD_DATA *cmd = nearMapGet(table, command, FALSE); if(cmd == NULL) return FALSE; else if(!*cmdGetUserGroup(cmd) || is_keyword(bitvectorGetBits(charGetUserGroups(ch)), cmdGetUserGroup(cmd), FALSE)) { if(charTryCmd(ch, cmd, arg) == -1) return FALSE; return TRUE; } else return FALSE; } else { // try to look up the possible commands LIST *cmdname_list = nearMapGetAllMatches(table, command); bool ret = FALSE; if(cmdname_list != NULL) { LIST_ITERATOR *cmdname_i = newListIterator(cmdname_list); CMD_DATA *cmd = NULL; const char *cmdname = NULL; ITERATE_LIST(cmdname, cmdname_i) { cmd = nearMapGet(table, cmdname, FALSE); if(!*cmdGetUserGroup(cmd) || is_keyword(bitvectorGetBits(charGetUserGroups(ch)), cmdGetUserGroup(cmd), FALSE)) { if(charTryCmd(ch, cmd, arg) != -1) ret = TRUE; break; } } deleteListIterator(cmdname_i); deleteListWith(cmdname_list, free); } return ret; } } void handle_cmd_input(SOCKET_DATA *dsock, char *arg) { CHAR_DATA *ch; if ((ch = socketGetChar(dsock)) == NULL) return; do_cmd(ch, arg, TRUE); } void do_cmd(CHAR_DATA *ch, char *arg, bool aliases_ok) { char command[MAX_BUFFER]; // make sure we've got a command to try if(!arg || !*arg) return; // if we are leading with a non-character, we are trying to do a short-form // command (e.g. ' for say, " for gossip). Just take the first character // and use the rest as the arg if(isalpha(*arg) || isdigit(*arg)) arg = one_arg(arg, command); else { *command = *arg; *(command+1) = '\0'; arg++; // and skip all spaces while(isspace(*arg)) arg++; } #ifdef MODULE_ALIAS if(aliases_ok && try_alias(ch, command, arg)) return; #endif // check to see if it's a faculty command #ifdef MODULE_FACULTY if(try_use_faculty(ch, command)) return; #endif // figure out what tables we need to look over LIST *cmd_tables = newList(); // item-specific commands here? <--- // character-specific commands here? <--- if(charGetRoom(ch) && roomHasCmds(charGetRoom(ch))) listQueue(cmd_tables, roomGetCmdTable(charGetRoom(ch))); // zone-specific commands here? <--- listQueue(cmd_tables, cmd_table); // go through each table in the list, in order. Check if the command exists // in any of the tables. Only allow abbreviations in the last table (the // game commands). Before a command is executed, run checks that might be // included on any of the previous tables int i, j, ret; bool found = FALSE; for(i = 0; i < listSize(cmd_tables); i++) { NEAR_MAP *table = listGet(cmd_tables, i); CMD_DATA *cmd = find_cmd(ch, table, command, (i == listSize(cmd_tables)-1 ? TRUE : FALSE)); if(cmd != NULL) { // first, run checks on all our previous tables for(j = 0; j < i; j++) { CMD_DATA *check = find_check(ch,listGet(cmd_tables,j),cmdGetName(cmd)); if(check != NULL) { // run the check if((ret = charTryCmd(ch, check, arg)) != -1) { if(ret == TRUE) hookRun("command", hookBuildInfo("ch str str", ch,cmdGetName(cmd),arg)); found = TRUE; break; } } } } if(found == TRUE) break; else if(cmd != NULL) { // execute the command if((ret = charTryCmd(ch, cmd, arg)) != -1) { if(ret == TRUE) hookRun("command",hookBuildInfo("ch str str",ch,cmdGetName(cmd),arg)); found = TRUE; break; } } } // nothing usable was found if(found == FALSE) text_to_char(ch, "Nie ma takiej komendy.\r\n"); // garbage collection deleteList(cmd_tables); /* // try the command if(!charGetRoom(ch) || !try_use_cmd_table(ch,roomGetCmdTable(charGetRoom(ch)),command,arg,FALSE)) if(!try_use_cmd_table(ch, cmd_table, command, arg, TRUE)) text_to_char(ch, "No such command.\r\n"); */ }