/************************************************************************** * File: act.movement.c Part of tbaMUD * * Usage: Movement commands, door handling, & sleep/rest/etc state. * * * * All rights reserved. See license complete information. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * **************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #include "handler.h" #include "db.h" #include "spells.h" #include "house.h" #include "constants.h" #include "dg_scripts.h" #include "act.h" #include "fight.h" #include "oasis.h" /* for buildwalk */ /* local only functions */ /* do_simple_move utility functions */ static int has_boat(struct char_data *ch); /* do_gen_door utility functions */ static int find_door(struct char_data *ch, const char *type, char *dir, const char *cmdname); static int has_key(struct char_data *ch, obj_vnum key); static void do_doorcmd(struct char_data *ch, struct obj_data *obj, int door, int scmd); static int ok_pick(struct char_data *ch, obj_vnum keynum, int pickproof, int scmd); /* simple function to determine if char can walk on water */ static int has_boat(struct char_data *ch) { struct obj_data *obj; int i; if (GET_LEVEL(ch) > LVL_IMMORT) return (1); if (AFF_FLAGGED(ch, AFF_WATERWALK) || AFF_FLAGGED(ch, AFF_FLYING)) return (1); /* non-wearable boats in inventory will do it */ for (obj = ch->carrying; obj; obj = obj->next_content) if (GET_OBJ_TYPE(obj) == ITEM_BOAT && (find_eq_pos(ch, obj, NULL) < 0)) return (1); /* and any boat you're wearing will do it too */ for (i = 0; i < NUM_WEARS; i++) if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_BOAT) return (1); return (0); } /* Simple function to determine if char can fly. */ int has_flight(struct char_data *ch) { struct obj_data *obj; int i; if (GET_LEVEL(ch) > LVL_IMMORT) return (1); if (AFF_FLAGGED(ch, AFF_FLYING)) return (1); /* Non-wearable flying items in inventory will do it. */ for (obj = ch->carrying; obj; obj = obj->next_content) if (OBJAFF_FLAGGED(obj, AFF_FLYING) && OBJAFF_FLAGGED(obj, AFF_FLYING)) return (1); /* Any equipped objects with AFF_FLYING will do it too. */ for (i = 0; i < NUM_WEARS; i++) if (GET_EQ(ch, i) && OBJAFF_FLAGGED(GET_EQ(ch, i), AFF_FLYING)) return (1); return (0); } /* Simple function to determine if char can scuba. */ int has_scuba(struct char_data *ch) { struct obj_data *obj; int i; if (GET_LEVEL(ch) > LVL_IMMORT) return (1); if (AFF_FLAGGED(ch, AFF_SCUBA)) return (1); /* Non-wearable scuba items in inventory will do it. */ for (obj = ch->carrying; obj; obj = obj->next_content) if (OBJAFF_FLAGGED(obj, AFF_SCUBA) && (find_eq_pos(ch, obj, NULL) < 0)) return (1); /* Any equipped objects with AFF_SCUBA will do it too. */ for (i = 0; i < NUM_WEARS; i++) if (GET_EQ(ch, i) && OBJAFF_FLAGGED(GET_EQ(ch, i), AFF_SCUBA)) return (1); return (0); } /** Move a PC/NPC character from their current location to a new location. This * is the standard movement locomotion function that all normal walking * movement by characters should be sent through. This function also defines * the move cost of normal locomotion as: * ( (move cost for source room) + (move cost for destination) ) / 2 * * @pre Function assumes that ch has no master controlling character, that * ch has no followers (in other words followers won't be moved by this * function) and that the direction traveled in is one of the valid, enumerated * direction. * @param ch The character structure to attempt to move. * @param dir The defined direction (NORTH, SOUTH, etc...) to attempt to * move into. * @param need_specials_check If TRUE will cause * @retval int 1 for a successful move (ch is now in a new location) * or 0 for a failed move (ch is still in the original location). */ int do_simple_move(struct char_data *ch, int dir, int need_specials_check) { /* Begin Local variable definitions */ /*---------------------------------------------------------------------*/ /* Used in our special proc check. By default, we pass a NULL argument * when checking for specials */ char spec_proc_args[MAX_INPUT_LENGTH] = ""; /* The room the character is currently in and will move from... */ room_rnum was_in = IN_ROOM(ch); /* ... and the room the character will move into. */ room_rnum going_to = EXIT(ch, dir)->to_room; /* How many movement points are required to travel from was_in to going_to. * We redefine this later when we need it. */ int need_movement = 0; /* Contains the "leave" message to display to the was_in room. */ char leave_message[SMALL_BUFSIZE]; /*---------------------------------------------------------------------*/ /* End Local variable definitions */ /* Begin checks that can prevent a character from leaving the was_in room. */ /* Future checks should be implemented within this section and return 0. */ /*---------------------------------------------------------------------*/ /* Check for special routines that might activate because of the move and * also might prevent the movement. Special requires commands, so we pass * in the "command" equivalent of the direction (ie. North is '1' in the * command list, but NORTH is defined as '0'). * Note -- only check if following; this avoids 'double spec-proc' bug */ if (need_specials_check && special(ch, dir + 1, spec_proc_args)) return 0; /* Leave Trigger Checks: Does a leave trigger block exit from the room? */ if (!leave_mtrigger(ch, dir) || IN_ROOM(ch) != was_in) /* prevent teleport crashes */ return 0; if (!leave_wtrigger(&world[IN_ROOM(ch)], ch, dir) || IN_ROOM(ch) != was_in) /* prevent teleport crashes */ return 0; if (!leave_otrigger(&world[IN_ROOM(ch)], ch, dir) || IN_ROOM(ch) != was_in) /* prevent teleport crashes */ return 0; /* Charm effect: Does it override the movement? */ if (AFF_FLAGGED(ch, AFF_CHARM) && ch->master && was_in == IN_ROOM(ch->master)) { send_to_char(ch, "The thought of leaving your master makes you weep.\r\n"); act("$n bursts into tears.", FALSE, ch, 0, 0, TO_ROOM); return (0); } /* Water, No Swimming Rooms: Does the deep water prevent movement? */ if ((SECT(was_in) == SECT_WATER_NOSWIM) || (SECT(going_to) == SECT_WATER_NOSWIM)) { if (!has_boat(ch)) { send_to_char(ch, "You need a boat to go there.\r\n"); return (0); } } /* Flying Required: Does lack of flying prevent movement? */ if ((SECT(was_in) == SECT_FLYING) || (SECT(going_to) == SECT_FLYING)) { if (!has_flight(ch)) { send_to_char(ch, "You need to be flying to go there!\r\n"); return (0); } } /* Underwater Room: Does lack of underwater breathing prevent movement? */ if ((SECT(was_in) == SECT_UNDERWATER) || (SECT(going_to) == SECT_UNDERWATER)) { if (!has_scuba(ch) && !IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)) { send_to_char(ch, "You need to be able to breathe water to go there!\r\n"); return (0); } } /* Houses: Can the player walk into the house? */ if (ROOM_FLAGGED(was_in, ROOM_ATRIUM)) { if (!House_can_enter(ch, GET_ROOM_VNUM(going_to))) { send_to_char(ch, "That's private property -- no trespassing!\r\n"); return (0); } } /* Check zone level recommendations */ if ((ZONE_MINLVL(GET_ROOM_ZONE(going_to)) != -1) && ZONE_MINLVL(GET_ROOM_ZONE(going_to)) > GET_LEVEL(ch)) { send_to_char(ch, "This zone is above your recommended level.\r\n"); } /* Check zone flag restrictions */ if (ZONE_FLAGGED(GET_ROOM_ZONE(going_to), ZONE_CLOSED)) { send_to_char(ch, "A mysterious barrier forces you back! That area is off-limits.\r\n"); return (0); } if (ZONE_FLAGGED(GET_ROOM_ZONE(going_to), ZONE_NOIMMORT) && (GET_LEVEL(ch) >= LVL_IMMORT) && (GET_LEVEL(ch) < LVL_GRGOD)) { send_to_char(ch, "A mysterious barrier forces you back! That area is off-limits.\r\n"); return (0); } /* Room Size Capacity: Is the room full of people already? */ if (ROOM_FLAGGED(going_to, ROOM_TUNNEL) && num_pc_in_room(&(world[going_to])) >= CONFIG_TUNNEL_SIZE) { if (CONFIG_TUNNEL_SIZE > 1) send_to_char(ch, "There isn't enough room for you to go there!\r\n"); else send_to_char(ch, "There isn't enough room there for more than one person!\r\n"); return (0); } /* Room Level Requirements: Is ch privileged enough to enter the room? */ if (ROOM_FLAGGED(going_to, ROOM_GODROOM) && GET_LEVEL(ch) < LVL_GOD) { send_to_char(ch, "You aren't godly enough to use that room!\r\n"); return (0); } /* All checks passed, nothing will prevent movement now other than lack of * move points. */ /* move points needed is avg. move loss for src and destination sect type */ need_movement = (movement_loss[SECT(was_in)] + movement_loss[SECT(going_to)]) / 2; /* Move Point Requirement Check */ if (GET_MOVE(ch) < need_movement && !IS_NPC(ch)) { if (need_specials_check && ch->master) send_to_char(ch, "You are too exhausted to follow.\r\n"); else send_to_char(ch, "You are too exhausted.\r\n"); return (0); } /*---------------------------------------------------------------------*/ /* End checks that can prevent a character from leaving the was_in room. */ /* Begin: the leave operation. */ /*---------------------------------------------------------------------*/ /* If applicable, subtract movement cost. */ if (GET_LEVEL(ch) < LVL_IMMORT && !IS_NPC(ch)) GET_MOVE(ch) -= need_movement; /* Generate the leave message and display to others in the was_in room. */ if (!AFF_FLAGGED(ch, AFF_SNEAK)) { snprintf(leave_message, sizeof(leave_message), "$n leaves %s.", dirs[dir]); act(leave_message, TRUE, ch, 0, 0, TO_ROOM); } char_from_room(ch); char_to_room(ch, going_to); /*---------------------------------------------------------------------*/ /* End: the leave operation. The character is now in the new room. */ /* Begin: Post-move operations. */ /*---------------------------------------------------------------------*/ /* Post Move Trigger Checks: Check the new room for triggers. * Assumptions: The character has already truly left the was_in room. If * the entry trigger "prevents" movement into the room, it is the triggers * job to provide a message to the original was_in room. */ if (!entry_mtrigger(ch) || !enter_wtrigger(&world[going_to], ch, dir)) { char_from_room(ch); char_to_room(ch, was_in); return 0; } /* Display arrival information to anyone in the destination room... */ if (!AFF_FLAGGED(ch, AFF_SNEAK)) act("$n has arrived.", TRUE, ch, 0, 0, TO_ROOM); /* ... and the room description to the character. */ if (ch->desc != NULL) look_at_room(ch, 0); /* ... and Kill the player if the room is a death trap. */ if (ROOM_FLAGGED(going_to, ROOM_DEATH) && GET_LEVEL(ch) < LVL_IMMORT) { mudlog(BRF, LVL_IMMORT, TRUE, "%s hit death trap #%d (%s)", GET_NAME(ch), GET_ROOM_VNUM(going_to), world[going_to].name); death_cry(ch); extract_char(ch); return (0); } /* At this point, the character is safe and in the room. */ /* Fire memory and greet triggers, check and see if the greet trigger * prevents movement, and if so, move the player back to the previous room. */ entry_memory_mtrigger(ch); if (!greet_mtrigger(ch, dir)) { char_from_room(ch); char_to_room(ch, was_in); look_at_room(ch, 0); /* Failed move, return a failure */ return (0); } else greet_memory_mtrigger(ch); /*---------------------------------------------------------------------*/ /* End: Post-move operations. */ /* Only here is the move successful *and* complete. Return success for * calling functions to handle post move operations. */ return (1); } int perform_move(struct char_data *ch, int dir, int need_specials_check) { room_rnum was_in; struct follow_type *k, *next; if (ch == NULL || dir < 0 || dir >= NUM_OF_DIRS || FIGHTING(ch)) return (0); else if (!CONFIG_DIAGONAL_DIRS && IS_DIAGONAL(dir)) send_to_char(ch, "Alas, you cannot go that way...\r\n"); else if ((!EXIT(ch, dir) && !buildwalk(ch, dir)) || EXIT(ch, dir)->to_room == NOWHERE) send_to_char(ch, "Alas, you cannot go that way...\r\n"); else if (EXIT_FLAGGED(EXIT(ch, dir), EX_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)))) { if (EXIT(ch, dir)->keyword) send_to_char(ch, "The %s seems to be closed.\r\n", fname(EXIT(ch, dir)->keyword)); else send_to_char(ch, "It seems to be closed.\r\n"); } else { if (!ch->followers) return (do_simple_move(ch, dir, need_specials_check)); was_in = IN_ROOM(ch); if (!do_simple_move(ch, dir, need_specials_check)) return (0); for (k = ch->followers; k; k = next) { next = k->next; if ((IN_ROOM(k->follower) == was_in) && (GET_POS(k->follower) >= POS_STANDING)) { act("You follow $N.\r\n", FALSE, k->follower, 0, ch, TO_CHAR); perform_move(k->follower, dir, 1); } } return (1); } return (0); } ACMD(do_move) { /* These subcmd defines are mapped precisely to the direction defines. */ perform_move(ch, subcmd, 0); } static int find_door(struct char_data *ch, const char *type, char *dir, const char *cmdname) { int door; if (*dir) { /* a direction was specified */ if ((door = search_block(dir, dirs, FALSE)) == -1) { /* Partial Match */ if ((door = search_block(dir, autoexits, FALSE)) == -1) { /* Check 'short' dirs too */ send_to_char(ch, "That's not a direction.\r\n"); return (-1); } } if (EXIT(ch, door)) { /* Braces added according to indent. -gg */ if (EXIT(ch, door)->keyword) { if (is_name(type, EXIT(ch, door)->keyword)) return (door); else { send_to_char(ch, "I see no %s there.\r\n", type); return (-1); } } else return (door); } else { send_to_char(ch, "I really don't see how you can %s anything there.\r\n", cmdname); return (-1); } } else { /* try to locate the keyword */ if (!*type) { send_to_char(ch, "What is it you want to %s?\r\n", cmdname); return (-1); } for (door = 0; door < DIR_COUNT; door++) { if (EXIT(ch, door)) { if (EXIT(ch, door)->keyword) { if (isname(type, EXIT(ch, door)->keyword)) { if ((!IS_NPC(ch)) && (!PRF_FLAGGED(ch, PRF_AUTODOOR))) return door; else if (is_abbrev(cmdname, "open")) { if (IS_SET(EXIT(ch, door)->exit_info, EX_CLOSED)) return door; else if (IS_SET(EXIT(ch, door)->exit_info, EX_LOCKED)) return door; } else if ((is_abbrev(cmdname, "close")) && (!(IS_SET(EXIT(ch, door)->exit_info, EX_CLOSED))) ) return door; else if ((is_abbrev(cmdname, "lock")) && (!(IS_SET(EXIT(ch, door)->exit_info, EX_LOCKED))) ) return door; else if ((is_abbrev(cmdname, "unlock")) && (IS_SET(EXIT(ch, door)->exit_info, EX_LOCKED)) ) return door; else if ((is_abbrev(cmdname, "pick")) && (IS_SET(EXIT(ch, door)->exit_info, EX_LOCKED)) ) return door; } } } } if ((!IS_NPC(ch)) && (!PRF_FLAGGED(ch, PRF_AUTODOOR))) send_to_char(ch, "There doesn't seem to be %s %s here.\r\n", AN(type), type); else if (is_abbrev(cmdname, "open")) send_to_char(ch, "There doesn't seem to be %s %s that can be opened.\r\n", AN(type), type); else if (is_abbrev(cmdname, "close")) send_to_char(ch, "There doesn't seem to be %s %s that can be closed.\r\n", AN(type), type); else if (is_abbrev(cmdname, "lock")) send_to_char(ch, "There doesn't seem to be %s %s that can be locked.\r\n", AN(type), type); else if (is_abbrev(cmdname, "unlock")) send_to_char(ch, "There doesn't seem to be %s %s that can be unlocked.\r\n", AN(type), type); else send_to_char(ch, "There doesn't seem to be %s %s that can be picked.\r\n", AN(type), type); return (-1); } } int has_key(struct char_data *ch, obj_vnum key) { struct obj_data *o; for (o = ch->carrying; o; o = o->next_content) if (GET_OBJ_VNUM(o) == key) return (1); if (GET_EQ(ch, WEAR_HOLD)) if (GET_OBJ_VNUM(GET_EQ(ch, WEAR_HOLD)) == key) return (1); return (0); } #define NEED_OPEN (1 << 0) #define NEED_CLOSED (1 << 1) #define NEED_UNLOCKED (1 << 2) #define NEED_LOCKED (1 << 3) /* cmd_door is required external from act.movement.c */ const char *cmd_door[] = { "open", "close", "unlock", "lock", "pick" }; static const int flags_door[] = { NEED_CLOSED | NEED_UNLOCKED, NEED_OPEN, NEED_CLOSED | NEED_LOCKED, NEED_CLOSED | NEED_UNLOCKED, NEED_CLOSED | NEED_LOCKED }; #define EXITN(room, door) (world[room].dir_option[door]) #define OPEN_DOOR(room, obj, door) ((obj) ?\ (REMOVE_BIT(GET_OBJ_VAL(obj, 1), CONT_CLOSED)) :\ (REMOVE_BIT(EXITN(room, door)->exit_info, EX_CLOSED))) #define CLOSE_DOOR(room, obj, door) ((obj) ?\ (SET_BIT(GET_OBJ_VAL(obj, 1), CONT_CLOSED)) :\ (SET_BIT(EXITN(room, door)->exit_info, EX_CLOSED))) #define LOCK_DOOR(room, obj, door) ((obj) ?\ (SET_BIT(GET_OBJ_VAL(obj, 1), CONT_LOCKED)) :\ (SET_BIT(EXITN(room, door)->exit_info, EX_LOCKED))) #define UNLOCK_DOOR(room, obj, door) ((obj) ?\ (REMOVE_BIT(GET_OBJ_VAL(obj, 1), CONT_LOCKED)) :\ (REMOVE_BIT(EXITN(room, door)->exit_info, EX_LOCKED))) #define TOGGLE_LOCK(room, obj, door) ((obj) ?\ (TOGGLE_BIT(GET_OBJ_VAL(obj, 1), CONT_LOCKED)) :\ (TOGGLE_BIT(EXITN(room, door)->exit_info, EX_LOCKED))) static void do_doorcmd(struct char_data *ch, struct obj_data *obj, int door, int scmd) { char buf[MAX_STRING_LENGTH]; size_t len; room_rnum other_room = NOWHERE; struct room_direction_data *back = NULL; if (!door_mtrigger(ch, scmd, door)) return; if (!door_wtrigger(ch, scmd, door)) return; len = snprintf(buf, sizeof(buf), "$n %ss ", cmd_door[scmd]); if (!obj && ((other_room = EXIT(ch, door)->to_room) != NOWHERE)) if ((back = world[other_room].dir_option[rev_dir[door]]) != NULL) if (back->to_room != IN_ROOM(ch)) back = NULL; switch (scmd) { case SCMD_OPEN: OPEN_DOOR(IN_ROOM(ch), obj, door); if (back) OPEN_DOOR(other_room, obj, rev_dir[door]); send_to_char(ch, "%s", CONFIG_OK); break; case SCMD_CLOSE: CLOSE_DOOR(IN_ROOM(ch), obj, door); if (back) CLOSE_DOOR(other_room, obj, rev_dir[door]); send_to_char(ch, "%s", CONFIG_OK); break; case SCMD_LOCK: LOCK_DOOR(IN_ROOM(ch), obj, door); if (back) LOCK_DOOR(other_room, obj, rev_dir[door]); send_to_char(ch, "*Click*\r\n"); break; case SCMD_UNLOCK: UNLOCK_DOOR(IN_ROOM(ch), obj, door); if (back) UNLOCK_DOOR(other_room, obj, rev_dir[door]); send_to_char(ch, "*Click*\r\n"); break; case SCMD_PICK: TOGGLE_LOCK(IN_ROOM(ch), obj, door); if (back) TOGGLE_LOCK(other_room, obj, rev_dir[door]); send_to_char(ch, "The lock quickly yields to your skills.\r\n"); len = strlcpy(buf, "$n skillfully picks the lock on ", sizeof(buf)); break; } /* Notify the room. */ if (len < sizeof(buf)) snprintf(buf + len, sizeof(buf) - len, "%s%s.", obj ? "" : "the ", obj ? "$p" : EXIT(ch, door)->keyword ? "$F" : "door"); if (!obj || IN_ROOM(obj) != NOWHERE) act(buf, FALSE, ch, obj, obj ? 0 : EXIT(ch, door)->keyword, TO_ROOM); /* Notify the other room */ if (back && (scmd == SCMD_OPEN || scmd == SCMD_CLOSE)) send_to_room(EXIT(ch, door)->to_room, "The %s is %s%s from the other side.\r\n", back->keyword ? fname(back->keyword) : "door", cmd_door[scmd], scmd == SCMD_CLOSE ? "d" : "ed"); } static int ok_pick(struct char_data *ch, obj_vnum keynum, int pickproof, int scmd) { int percent, skill_lvl; if (scmd != SCMD_PICK) return (1); percent = rand_number(1, 101); skill_lvl = GET_SKILL(ch, SKILL_PICK_LOCK) + dex_app_skill[GET_DEX(ch)].p_locks; if (keynum == NOTHING) send_to_char(ch, "Odd - you can't seem to find a keyhole.\r\n"); else if (pickproof) send_to_char(ch, "It resists your attempts to pick it.\r\n"); else if (percent > skill_lvl) send_to_char(ch, "You failed to pick the lock.\r\n"); else return (1); return (0); } #define DOOR_IS_OPENABLE(ch, obj, door) ((obj) ? ((GET_OBJ_TYPE(obj) == \ ITEM_CONTAINER) && OBJVAL_FLAGGED(obj, CONT_CLOSEABLE)) :\ (EXIT_FLAGGED(EXIT(ch, door), EX_ISDOOR))) #define DOOR_IS_OPEN(ch, obj, door) ((obj) ? (!OBJVAL_FLAGGED(obj, \ CONT_CLOSED)) : (!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED))) #define DOOR_IS_UNLOCKED(ch, obj, door) ((obj) ? (!OBJVAL_FLAGGED(obj, \ CONT_LOCKED)) : (!EXIT_FLAGGED(EXIT(ch, door), EX_LOCKED))) #define DOOR_IS_PICKPROOF(ch, obj, door) ((obj) ? (OBJVAL_FLAGGED(obj, \ CONT_PICKPROOF)) : (EXIT_FLAGGED(EXIT(ch, door), EX_PICKPROOF))) #define DOOR_IS_CLOSED(ch, obj, door) (!(DOOR_IS_OPEN(ch, obj, door))) #define DOOR_IS_LOCKED(ch, obj, door) (!(DOOR_IS_UNLOCKED(ch, obj, door))) #define DOOR_KEY(ch, obj, door) ((obj) ? (GET_OBJ_VAL(obj, 2)) : \ (EXIT(ch, door)->key)) ACMD(do_gen_door) { int door = -1; obj_vnum keynum; char type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH]; struct obj_data *obj = NULL; struct char_data *victim = NULL; skip_spaces(&argument); if (!*argument) { send_to_char(ch, "%c%s what?\r\n", UPPER(*cmd_door[subcmd]), cmd_door[subcmd] + 1); return; } two_arguments(argument, type, dir); if (!generic_find(type, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj)) door = find_door(ch, type, dir, cmd_door[subcmd]); if ((obj) && (GET_OBJ_TYPE(obj) != ITEM_CONTAINER)) { obj = NULL; door = find_door(ch, type, dir, cmd_door[subcmd]); } if ((obj) || (door >= 0)) { keynum = DOOR_KEY(ch, obj, door); if (!(DOOR_IS_OPENABLE(ch, obj, door))) send_to_char(ch, "You can't %s that!\r\n", cmd_door[subcmd]); else if (!DOOR_IS_OPEN(ch, obj, door) && IS_SET(flags_door[subcmd], NEED_OPEN)) send_to_char(ch, "But it's already closed!\r\n"); else if (!DOOR_IS_CLOSED(ch, obj, door) && IS_SET(flags_door[subcmd], NEED_CLOSED)) send_to_char(ch, "But it's currently open!\r\n"); else if (!(DOOR_IS_LOCKED(ch, obj, door)) && IS_SET(flags_door[subcmd], NEED_LOCKED)) send_to_char(ch, "Oh.. it wasn't locked, after all..\r\n"); else if (!(DOOR_IS_UNLOCKED(ch, obj, door)) && IS_SET(flags_door[subcmd], NEED_UNLOCKED) && ((!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOKEY))) && (has_key(ch, keynum)) ) { send_to_char(ch, "It is locked, but you have the key.\r\n"); send_to_char(ch, "*Click*\r\n"); do_doorcmd(ch, obj, door, subcmd); } else if (!(DOOR_IS_UNLOCKED(ch, obj, door)) && IS_SET(flags_door[subcmd], NEED_UNLOCKED) && ((!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOKEY))) && (!has_key(ch, keynum)) ) { send_to_char(ch, "It is locked, and you do not have the key!\r\n"); } else if (!(DOOR_IS_UNLOCKED(ch, obj, door)) && IS_SET(flags_door[subcmd], NEED_UNLOCKED) && (GET_LEVEL(ch) < LVL_IMMORT || (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)))) send_to_char(ch, "It seems to be locked.\r\n"); else if (!has_key(ch, keynum) && (GET_LEVEL(ch) < LVL_GOD) && ((subcmd == SCMD_LOCK) || (subcmd == SCMD_UNLOCK))) send_to_char(ch, "You don't seem to have the proper key.\r\n"); else if (ok_pick(ch, keynum, DOOR_IS_PICKPROOF(ch, obj, door), subcmd)) do_doorcmd(ch, obj, door, subcmd); } return; } ACMD(do_enter) { char buf[MAX_INPUT_LENGTH]; int door; one_argument(argument, buf); if (*buf) { /* an argument was supplied, search for door * keyword */ for (door = 0; door < DIR_COUNT; door++) if (EXIT(ch, door)) if (EXIT(ch, door)->keyword) if (!str_cmp(EXIT(ch, door)->keyword, buf)) { perform_move(ch, door, 1); return; } send_to_char(ch, "There is no %s here.\r\n", buf); } else if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_INDOORS)) send_to_char(ch, "You are already indoors.\r\n"); else { /* try to locate an entrance */ for (door = 0; door < DIR_COUNT; door++) if (EXIT(ch, door)) if (EXIT(ch, door)->to_room != NOWHERE) if (!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED) && ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_INDOORS)) { perform_move(ch, door, 1); return; } send_to_char(ch, "You can't seem to find anything to enter.\r\n"); } } ACMD(do_leave) { int door; if (OUTSIDE(ch)) send_to_char(ch, "You are outside.. where do you want to go?\r\n"); else { for (door = 0; door < DIR_COUNT; door++) if (EXIT(ch, door)) if (EXIT(ch, door)->to_room != NOWHERE) if (!EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED) && !ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_INDOORS)) { perform_move(ch, door, 1); return; } send_to_char(ch, "I see no obvious exits to the outside.\r\n"); } } ACMD(do_stand) { switch (GET_POS(ch)) { case POS_STANDING: send_to_char(ch, "You are already standing.\r\n"); break; case POS_SITTING: send_to_char(ch, "You stand up.\r\n"); act("$n clambers to $s feet.", TRUE, ch, 0, 0, TO_ROOM); /* Were they sitting in something? */ char_from_furniture(ch); /* Will be sitting after a successful bash and may still be fighting. */ GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING; break; case POS_RESTING: send_to_char(ch, "You stop resting, and stand up.\r\n"); act("$n stops resting, and clambers on $s feet.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_STANDING; /* Were they sitting in something. */ char_from_furniture(ch); break; case POS_SLEEPING: send_to_char(ch, "You have to wake up first!\r\n"); break; case POS_FIGHTING: send_to_char(ch, "Do you not consider fighting as standing?\r\n"); break; default: send_to_char(ch, "You stop floating around, and put your feet on the ground.\r\n"); act("$n stops floating around, and puts $s feet on the ground.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_STANDING; break; } } ACMD(do_sit) { char arg[MAX_STRING_LENGTH]; struct obj_data *furniture; struct char_data *tempch; int found; one_argument(argument, arg); if (!*arg) found = 0; if (!(furniture = get_obj_in_list_vis(ch, arg, NULL, world[ch->in_room].contents))) found = 0; else found = 1; switch (GET_POS(ch)) { case POS_STANDING: if (found == 0) { send_to_char(ch, "You sit down.\r\n"); act("$n sits down.", FALSE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; } else { if (GET_OBJ_TYPE(furniture) != ITEM_FURNITURE) { send_to_char(ch, "You can't sit on that!\r\n"); return; } else if (GET_OBJ_VAL(furniture, 1) > GET_OBJ_VAL(furniture, 0)) { /* Val 1 is current number sitting, 0 is max in sitting. */ act("$p looks like it's all full.", TRUE, ch, furniture, 0, TO_CHAR); log("SYSERR: Furniture %d holding too many people.", GET_OBJ_VNUM(furniture)); return; } else if (GET_OBJ_VAL(furniture, 1) == GET_OBJ_VAL(furniture, 0)) { act("There is no where left to sit upon $p.", TRUE, ch, furniture, 0, TO_CHAR); return; } else { if (OBJ_SAT_IN_BY(furniture) == NULL) OBJ_SAT_IN_BY(furniture) = ch; for (tempch = OBJ_SAT_IN_BY(furniture); tempch != ch ; tempch = NEXT_SITTING(tempch)) { if (NEXT_SITTING(tempch)) continue; NEXT_SITTING(tempch) = ch; } act("You sit down upon $p.", TRUE, ch, furniture, 0, TO_CHAR); act("$n sits down upon $p.", TRUE, ch, furniture, 0, TO_ROOM); SITTING(ch) = furniture; NEXT_SITTING(ch) = NULL; GET_OBJ_VAL(furniture, 1) += 1; GET_POS(ch) = POS_SITTING; } } break; case POS_SITTING: send_to_char(ch, "You're sitting already.\r\n"); break; case POS_RESTING: send_to_char(ch, "You stop resting, and sit up.\r\n"); act("$n stops resting.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; break; case POS_SLEEPING: send_to_char(ch, "You have to wake up first.\r\n"); break; case POS_FIGHTING: send_to_char(ch, "Sit down while fighting? Are you MAD?\r\n"); break; default: send_to_char(ch, "You stop floating around, and sit down.\r\n"); act("$n stops floating around, and sits down.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; break; } } ACMD(do_rest) { switch (GET_POS(ch)) { case POS_STANDING: send_to_char(ch, "You sit down and rest your tired bones.\r\n"); act("$n sits down and rests.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; break; case POS_SITTING: send_to_char(ch, "You rest your tired bones.\r\n"); act("$n rests.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; break; case POS_RESTING: send_to_char(ch, "You are already resting.\r\n"); break; case POS_SLEEPING: send_to_char(ch, "You have to wake up first.\r\n"); break; case POS_FIGHTING: send_to_char(ch, "Rest while fighting? Are you MAD?\r\n"); break; default: send_to_char(ch, "You stop floating around, and stop to rest your tired bones.\r\n"); act("$n stops floating around, and rests.", FALSE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; break; } } ACMD(do_sleep) { switch (GET_POS(ch)) { case POS_STANDING: case POS_SITTING: case POS_RESTING: send_to_char(ch, "You go to sleep.\r\n"); act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SLEEPING; break; case POS_SLEEPING: send_to_char(ch, "You are already sound asleep.\r\n"); break; case POS_FIGHTING: send_to_char(ch, "Sleep while fighting? Are you MAD?\r\n"); break; default: send_to_char(ch, "You stop floating around, and lie down to sleep.\r\n"); act("$n stops floating around, and lie down to sleep.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SLEEPING; break; } } ACMD(do_wake) { char arg[MAX_INPUT_LENGTH]; struct char_data *vict; int self = 0; one_argument(argument, arg); if (*arg) { if (GET_POS(ch) == POS_SLEEPING) send_to_char(ch, "Maybe you should wake yourself up first.\r\n"); else if ((vict = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM)) == NULL) send_to_char(ch, "%s", CONFIG_NOPERSON); else if (vict == ch) self = 1; else if (AWAKE(vict)) act("$E is already awake.", FALSE, ch, 0, vict, TO_CHAR); else if (AFF_FLAGGED(vict, AFF_SLEEP)) act("You can't wake $M up!", FALSE, ch, 0, vict, TO_CHAR); else if (GET_POS(vict) < POS_SLEEPING) act("$E's in pretty bad shape!", FALSE, ch, 0, vict, TO_CHAR); else { act("You wake $M up.", FALSE, ch, 0, vict, TO_CHAR); act("You are awakened by $n.", FALSE, ch, 0, vict, TO_VICT | TO_SLEEP); GET_POS(vict) = POS_SITTING; } if (!self) return; } if (AFF_FLAGGED(ch, AFF_SLEEP)) send_to_char(ch, "You can't wake up!\r\n"); else if (GET_POS(ch) > POS_SLEEPING) send_to_char(ch, "You are already awake...\r\n"); else { send_to_char(ch, "You awaken, and sit up.\r\n"); act("$n awakens.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; } } ACMD(do_follow) { char buf[MAX_INPUT_LENGTH]; struct char_data *leader; one_argument(argument, buf); if (*buf) { if (!(leader = get_char_vis(ch, buf, NULL, FIND_CHAR_ROOM))) { send_to_char(ch, "%s", CONFIG_NOPERSON); return; } } else { send_to_char(ch, "Whom do you wish to follow?\r\n"); return; } if (ch->master == leader) { act("You are already following $M.", FALSE, ch, 0, leader, TO_CHAR); return; } if (AFF_FLAGGED(ch, AFF_CHARM) && (ch->master)) { act("But you only feel like following $N!", FALSE, ch, 0, ch->master, TO_CHAR); } else { /* Not Charmed follow person */ if (leader == ch) { if (!ch->master) { send_to_char(ch, "You are already following yourself.\r\n"); return; } stop_follower(ch); } else { if (circle_follow(ch, leader)) { send_to_char(ch, "Sorry, but following in loops is not allowed.\r\n"); return; } if (ch->master) stop_follower(ch); add_follower(ch, leader); } } }