//***************************************************************************** // // cmd_manip.c // // a set of commands that NakedMud(tm) comes with that allows characters to // manipulate various things. These commands are mostly directed towards // manipulating objects (e.g. get, put, drop, etc...) but can also affect other // things like exits (e.g. open, close) // //***************************************************************************** #include "mud.h" #include "utils.h" #include "handler.h" #include "inform.h" #include "character.h" #include "exit.h" #include "world.h" #include "room.h" #include "object.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "items/items.h" #include "items/container.h" //***************************************************************************** // local functions //***************************************************************************** // // used by open, close, lock, and unlock. When an exit is manipulated on one // side, it is the case that we'll want to do an identical manipulation on the // other side. That's what we do here. Note: Can only do close OR lock with // one call to this function. Cannot handle both at the same time! void try_manip_other_exit(ROOM_DATA *room, EXIT_DATA *exit, bool closed, bool locked) { // see if there's a room on the other side ROOM_DATA *to = worldGetRoom(gameworld, exitGetTo(exit)); EXIT_DATA *other_exit = NULL; const char *opp_dir = NULL; if(to == NULL) return; // check to see if we can figure out a return direction if(*exitGetOpposite(exit)) opp_dir = exitGetOpposite(exit); else { int opp_num = dirGetNum(roomGetExitDir(room, exit)); if(opp_num != DIR_NONE) opp_dir = dirGetName(dirGetOpposite(opp_num)); } // do we have an opposite direction to manipulate? if(opp_dir == NULL) return; // do we have an opposite exit to manipulate? if( (other_exit = roomGetExit(to, opp_dir)) != NULL) { // are we changing the close state, and the exit's not locked? if(exitIsClosed(other_exit) != closed && !exitIsLocked(other_exit)) { exitSetClosed(other_exit, closed); send_to_list(roomGetCharacters(to), "%s %s from the other side.\r\n", (*exitGetName(other_exit)?exitGetName(other_exit): "An exit"), (closed ? "closes" : "opens")); } // are we changing the lock state, and the exit is closed? if(exitIsLocked(other_exit) != locked && exitIsClosed(other_exit)) { exitSetLocked(other_exit, locked); send_to_list(roomGetCharacters(to), "%s %s from the other side.\r\n", (*exitGetName(other_exit)?exitGetName(other_exit): "An exit"), (locked ? "locks" : "unlocks")); } } } //***************************************************************************** // player commands //***************************************************************************** // // try to lock an exit or container. The container can be anything in our // immediate visible range (room, inventory, body). do_lock automatically // checks if we have the key on us. // // examples: // lock door lock a door in the room // lock south lock the south exit // lock 2.chest lock the 2nd chest in our visible range COMMAND(cmd_lock) { int found_type = PARSE_NONE; void *found = NULL; if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv.eq exit }", &found, &found_type)) return; // did we find an exit or an object? if(found_type == PARSE_EXIT) { if(!exitIsClosed(found)) send_to_char(ch, "%s must be closed first.\r\n", exitGetName(found)); else if(exitIsLocked(found)) send_to_char(ch, "%s is already locked.\r\n", exitGetName(found)); else if(!*exitGetKey(found)) send_to_char(ch, "You cannot figure out how %s would be locked.\r\n", exitGetName(found)); else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), get_key_locale(roomGetClass(charGetRoom(ch)))))) send_to_char(ch, "You cannot seem to find the key.\r\n"); else { send_to_char(ch, "You lock %s.\r\n", exitGetName(found)); send_around_char(ch, TRUE, "%s locks %s.\r\n", charGetName(ch), exitGetName(found)); exitSetLocked(found, TRUE); // and try the other side try_manip_other_exit(charGetRoom(ch), found, exitIsClosed(found), TRUE); } } // object found else { // if(found_type == PARSE_OBJ) { if(!objIsType(found, "container")) send_to_char(ch, "%s is not a container.\r\n", objGetName(found)); else if(!containerIsClosed(found)) send_to_char(ch, "%s must be closed first.\r\n", objGetName(found)); else if(containerIsLocked(found)) send_to_char(ch, "%s is already locked.\r\n", objGetName(found)); else if(!*containerGetKey(found)) send_to_char(ch, "You cannot figure out how %s would be locked.\r\n", objGetName(found)); else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found), get_key_locale(objGetClass(found))))) send_to_char(ch, "You cannot seem to find the key.\r\n"); else { send_to_char(ch, "You lock %s.\r\n", objGetName(found)); message(ch, NULL, found, NULL, TRUE, TO_ROOM, "$n locks $o."); containerSetLocked(found, TRUE); } } } // // the opposite of lock COMMAND(cmd_unlock) { int found_type = PARSE_NONE; void *found = NULL; if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv exit }", &found, &found_type)) return; // did we find something? if(found_type == PARSE_EXIT) { if(!exitIsLocked(found)) send_to_char(ch, "%s is not locked.\r\n", exitGetName(found)); else if(!*exitGetKey(found)) send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n", exitGetName(found)); else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), get_key_locale(roomGetClass(charGetRoom(ch)))))) send_to_char(ch, "You cannot seem to find the key.\r\n"); else { send_to_char(ch, "You unlock %s.\r\n", exitGetName(found)); send_around_char(ch, TRUE, "%s unlocks %s.\r\n", charGetName(ch), exitGetName(found)); exitSetLocked(found, FALSE); // and try the other side try_manip_other_exit(charGetRoom(ch), found, exitIsClosed(found), FALSE); } } else { // if(found_type == PARSE_OBJ) { if(!objIsType(found, "container")) send_to_char(ch, "%s is not a container.\r\n", objGetName(found)); else if(!containerIsLocked(found)) send_to_char(ch, "%s is not locked.\r\n", objGetName(found)); else if(!*containerGetKey(found)) send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n", objGetName(found)); else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found), get_key_locale(objGetClass(found))))) send_to_char(ch, "You cannot seem to find the key.\r\n"); else { send_to_char(ch, "You unlock %s.\r\n", objGetName(found)); message(ch, NULL, found, NULL, TRUE, TO_ROOM, "$n unlocks $o."); containerSetLocked(found, FALSE); } } } // // put one thing into another. The thing you wish to put must be in // your inventory. The container must be in your immediate visible range // (room, inventory, body) // // usage: put [the] <thing> [in the] <container> // // examples: // put coin bag put a coin into the bag // put all.shirt closet put all of the shirts in the closet COMMAND(cmd_put) { void *found = NULL; bool multiple = FALSE; OBJ_DATA *cont = NULL; if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple [in the] obj.room.inv", &found, &multiple, &cont)) return; // make sure we have a container if(!objIsType(cont, "container")) send_to_char(ch, "%s is not a container.\r\n", objGetName(cont)); // did we find a list of things or a single item? else if(multiple == FALSE) do_put(ch, found, cont); // we have to move a bunch of things else { OBJ_DATA *obj = NULL; while( (obj = listPop(found)) != NULL) do_put(ch, obj, cont); deleteList(found); } } // // attempt to open a door or container. The container must be in our immediate // visible range (room, inventory, body). // // usage: open [the] <thing> // // examples: // open door open a door // open 2.bag open your second bag // open east open the east exit // open backpack on self open a backpack you are wearing COMMAND(cmd_open) { void *found = NULL; int found_type = PARSE_NONE; if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.inv.eq exit }", &found, &found_type)) return; // open an exit if(found_type == PARSE_EXIT) { if(!exitIsClosable(found)) send_to_char(ch, "But %s cannot be opened!\r\n", (*exitGetName(found) ? exitGetName(found) : "it")); else if(!exitIsClosed(found)) send_to_char(ch, "It is it easy to open something when someone " "has already done it for you!\r\n"); else if(exitIsLocked(found)) send_to_char(ch, "%s appears to be locked.\r\n", (*exitGetName(found) ? exitGetName(found) : "It")); else { mssgprintf(ch, NULL, NULL, NULL, FALSE, TO_ROOM, "$n opens %s.", (*exitGetName(found) ? exitGetName(found) : "an exit")); send_to_char(ch, "You open %s.\r\n", (*exitGetName(found) ? exitGetName(found) : "the exit")); exitSetClosed(found, FALSE); // try opening the other side try_manip_other_exit(charGetRoom(ch), found, FALSE, exitIsLocked(found)); } } // open a container else { // if(found_type == FOUND_OBJ) { // make sure it's a container and it can be opened if(!objIsType(found, "container") || !containerIsClosable(found)) send_to_char(ch, "But it cannot be opened!\r\n"); else if(!containerIsClosed(found)) send_to_char(ch, "It is already opened.\r\n"); else if(containerIsLocked(found)) send_to_char(ch, "It appears to be locked.\r\n"); else { send_to_char(ch, "You open %s.\r\n", objGetName(found)); message(ch, NULL, found, NULL, FALSE, TO_ROOM, "$n opens $o."); containerSetClosed(found, FALSE); } } } // // cmd_close is used to close containers and exits. // usage: open <thing> // // examples: // close door close a door // close 2.bag close your second bag // close east close the east exit // close backpack on self close a backpack you are wearing COMMAND(cmd_close) { void *found = NULL; int found_type = PARSE_NONE; if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.eq.inv exit }", &found, &found_type)) return; // close an exit if(found_type == PARSE_EXIT) { if(!exitIsClosable(found)) send_to_char(ch, "But %s cannot be closed!\r\n", (*exitGetName(found) ? exitGetName(found) : "it")); else if(exitIsClosed(found)) send_to_char(ch, "It is easy to close something when someone " "has already done it for you!\r\n"); else { char other_buf[SMALL_BUFFER]; sprintf(other_buf, "$n closes %s.", (*exitGetName(found) ? exitGetName(found) : "an exit")); message(ch, NULL, NULL, NULL, FALSE, TO_ROOM, other_buf); send_to_char(ch, "You close %s.\r\n", (*exitGetName(found) ? exitGetName(found) : "the exit")); exitSetClosed(found, TRUE); // try opening the other side try_manip_other_exit(charGetRoom(ch), found, TRUE, exitIsLocked(found)); } } // close a container else { // if(found_type == PARSE_OBJ) { // make sure it's a container and it can be closed if(!objIsType(found, "container") || !containerIsClosable(found)) send_to_char(ch, "But it cannot even be closed!\r\n"); else if(containerIsClosed(found)) send_to_char(ch, "It is already closed.\r\n"); else { send_to_char(ch, "You close %s.\r\n", objGetName(found)); message(ch, NULL, found, NULL, FALSE, TO_ROOM, "$n closes $o."); containerSetClosed(found, TRUE); } } } // // cmd_get is used to move objects from containers or the room to your inventory // usage: get <object> <from> // // examples: // get sword get a sword from the room // get 2.cupcake bag get the second cupcake from your bag // get all.coin get all of the coins on the ground COMMAND(cmd_get) { if(!arg || !*arg) { send_to_char(ch, "What did you want to get?\r\n"); return; } // the name of what we're trying to get char name[SMALL_BUFFER]; arg = one_arg(arg, name); // first check to see if we're trying to get from a container OBJ_DATA *cont = NULL; if(*arg) { cont = generic_find(ch, arg, FIND_TYPE_OBJ, FIND_SCOPE_IMMEDIATE, FALSE, NULL); // were we trying to get something from a container // but couldn't find the container? if(cont == NULL) { send_to_char(ch, "Get what from what?\r\n"); return; } else if(!objIsType(cont, "container")) { send_to_char(ch, "%s is not a container.\r\n", objGetName(cont)); return; } else if(containerIsClosed(cont)) { send_to_char(ch, "%s is closed. Try opening it first.\r\n", objGetName(cont)); return; } } int found_type = FOUND_NONE; void *found = NULL; // if we have a container, just search the container for things if(cont != NULL) { // oi... this is going to get messy. We have to do some stuff // that the generic_find will usually do for us - i.e. checking // to see if we need to get a list of items or a single item // we should really add a new function in the handler for doing this int count = 1; get_count(name, name, &count); if(count == COUNT_ALL) { found_type = FOUND_LIST; found = find_all_objs(ch, objGetContents(cont), name, NULL, TRUE); } else { found_type = FOUND_OBJ; found = find_obj(ch, objGetContents(cont), count, name, NULL, TRUE); } } // otherwise, search the room for visible things else found = generic_find(ch, name, FIND_TYPE_OBJ, FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE, TRUE, &found_type); if(found && found_type == FOUND_OBJ) do_get(ch, found, cont); else if(found && found_type == FOUND_LIST) { OBJ_DATA *obj = NULL; while( (obj = listPop(found)) != NULL) do_get(ch, obj, cont); deleteList(found); } else send_to_char(ch, "You can't find what you're looking for.\r\n"); } // // cmd_give is used to transfer an object in your possession to // another character // usage: give [the] <object> [to] <person> // // examples: // give doll girl give a doll in your inventory to a girl // give all.coin robber give all of your money to the robber // COMMAND(cmd_give) { CHAR_DATA *recv = NULL; // the person we're giving stuff to void *to_give = NULL; // may be a list or a single item bool multiple = FALSE; // are we giving one or multiple items? // try to give objects from our inventory. We can give multiple items. Give // them to a person in the room who is not ourself. The fact we can see the // receiver is implied. If we fail to find our items or receiver, parse_args // will tell the character what he did wrong, and we will halt the command if(!parse_args(ch,TRUE,cmd,arg, "[the] obj.inv.multiple [to] ch.room.noself", &to_give, &multiple, &recv)) return; // just a single item to give... if(multiple == FALSE) do_give(ch, recv, to_give); // we have a list of items to give else { LIST_ITERATOR *obj_i = newListIterator(to_give); OBJ_DATA *obj = NULL; ITERATE_LIST(obj, obj_i) { do_give(ch, recv, obj); } deleteListIterator(obj_i); // we also have to delete the list that parse_args sent us deleteList(to_give); } } // // cmd_drop is used to transfer an object in your inventory to the ground // usage: drop <item> // // examples: // drop bag drop a bag you have // drop all.bread drop all of the bread you are carrying // drop 2.cupcake drop the second cupcake in your posession COMMAND(cmd_drop) { void *found = NULL; bool multiple = FALSE; if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple",&found,&multiple)) return; // are we dropping a list of things, or just one? if(multiple == FALSE) do_drop(ch, found); // we got a list of things... drop 'em all else { OBJ_DATA *obj = NULL; while( (obj = listPop(found)) != NULL) do_drop(ch, obj); deleteList(found); } } // // cmd_wear is used to equip wearable items in your inventory to your body // usage: wear [object] [where] // // examples: // wear shirt equip a shirt // wear all.ring wear all of the rings in your // inventory // wear gloves left hand, right hand wear the gloves on your left and // right hands COMMAND(cmd_wear) { void *found = NULL; bool multiple = FALSE; char *where = NULL; if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple | [on my] string", &found, &multiple, &where)) return; // are we wearing one thing, or multiple things? if(multiple == FALSE) do_wear(ch, found, where); // we're trying to wear multiple items else { OBJ_DATA *obj = NULL; while( (obj = listPop(found)) != NULL) do_wear(ch, obj, where); deleteList(found); } } // // cmd_remove is used to unequip items on your body to your inventory // usage: remove <item> // // examples: // remove mask remove the mask you are wearing // remove all.ring remove all the rings you have on // remove 2.ring remove the 2nd ring you have equipped COMMAND(cmd_remove) { void *found = NULL; bool multiple = FALSE; if(!parse_args(ch, TRUE, cmd, arg, "obj.eq.multiple", &found, &multiple)) return; // are we trying to remove one thing, or multiple things? if(multiple == FALSE) do_remove(ch, found); // removing multiple things... else { OBJ_DATA *obj = NULL; while( (obj = listPop(found)) != NULL) do_remove(ch, obj); deleteList(found); } }