static int chown_ok(dbref player, dbref thing, dbref newowner, NEW_PE_INFO *pe_info) { /* Can't touch garbage */ if (IsGarbage(thing)) return 0; /* Wizards can do it all */ if (Wizard(player)) return 1; /* In order for non-wiz player to @chown thing to newowner, * player must control newowner or newowner must be a Zone Master * and player must pass its zone lock. * * In addition, one of the following must apply: * 1. player owns thing, or * 2. player controls Owner(thing), newowner is a zone master, * and Owner(thing) passes newowner's zone-lock, or * 3. thing is CHOWN_OK, and player holds thing if it's an object. * * The third condition is syntactic sugar to handle the situation * where Joe owns Box, an ordinary object, and Tool, an inherit object, * and ZMP, a Zone Master Player, is zone-locked to =tool. * In this case, if Joe doesn't pass ZMP's lock, we don't want * Joe to be able to @fo Tool=@chown Box=ZMP */ /* Does player control newowner, or is newowner a Zone Master and player * passes the lock? */ if (!(controls(player, newowner) || (ZMaster(newowner) && eval_lock_with(player, newowner, Zone_Lock, pe_info)))) return 0; /* Target player is legitimate. Does player control the object? */ if (Owns(player, thing)) return 1; if (controls(player, Owner(thing)) && ZMaster(newowner) && eval_lock_with(Owner(thing), newowner, Zone_Lock, pe_info)) return 1; if ((!IsThing(thing) || (Location(thing) == player)) && ChownOk(thing) && eval_lock_with(player, thing, Chown_Lock, pe_info)) return 1; return 0; }
/** lock evaluation -- determines if player passes lock on thing, for * the purposes of picking up an object or moving through an exit. * \param player to check against lock. * \param thing thing to check the basic lock on. * \param pe_info the pe_info for Basic lock evaluation * \retval 1 player passes lock. * \retval 0 player fails lock. */ int could_doit(dbref player, dbref thing, NEW_PE_INFO *pe_info) { if (!IsRoom(thing) && Location(thing) == NOTHING) return 0; return (eval_lock_with(player, thing, Basic_Lock, pe_info)); }
/** Can player pemit to target? * You can pemit if you're pemit_all, if you're pemitting to yourself, * if you're pemitting to a non-player, or if you pass target's * pagelock and target isn't HAVEN. * \param player dbref attempting to pemit. * \param target target dbref to pemit to. * \param dofails If nonzero, send failure message 'def' or run fail_lock() * \param def show a default message if there is no appropriate failure message? * \param pe_info the pe_info for page lock evaluation * \retval 1 player may pemit to target. * \retval 0 player may not pemit to target. */ int okay_pemit(dbref player, dbref target, int dofails, int def, NEW_PE_INFO *pe_info) { char defmsg[BUFFER_LEN]; char *dp = NULL; if (Pemit_All(player)) return 1; if (dofails && def) { dp = defmsg; safe_format(defmsg, &dp, T("I'm sorry, but %s wishes to be left alone now."), AName(target, AN_SYS, NULL)); *dp = '\0'; dp = defmsg; } if (IsPlayer(target) && Haven(target)) { if (dofails && def) notify(player, dp); return 0; } if (!eval_lock_with(player, target, Page_Lock, pe_info)) { if (dofails) { fail_lock(player, target, Page_Lock, dp, NOTHING); } return 0; } return 1; }
/** The teach command. * \param player the enactor. * \param tbuf1 the command being taught. * \param list is tbuf1 an action list, or a single command? * \param parent_queue the queue entry to run the command in */ void do_teach(dbref player, const char *tbuf1, int list, MQUE *parent_queue) { dbref loc; int flags = QUEUE_RECURSE; char lesson[BUFFER_LEN], *lp; loc = speech_loc(player); if (!GoodObject(loc)) return; if (!Loud(player) && !eval_lock_with(player, loc, Speech_Lock, parent_queue->pe_info)) { fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING); return; } if (!tbuf1 || !*tbuf1) { notify(player, T("What command do you want to teach?")); return; } if (!list) flags |= QUEUE_NOLIST; lp = lesson; safe_format(lesson, &lp, T("%s types --> %s%s%s"), spname(player), ANSI_HILITE, tbuf1, ANSI_END); *lp = '\0'; notify_anything(player, player, na_loc, &loc, NULL, NA_INTER_HEAR | NA_PROPAGATE, lesson, NULL, loc, NULL); new_queue_actionlist(player, parent_queue->enactor, player, (char *) tbuf1, parent_queue, PE_INFO_SHARE, flags, NULL); }
/** Evaluate a lock, saving/clearing the env (%0-%9) and qreg (%q*) first, ** and restoring them after. */ int eval_lock_clear(dbref player, dbref thing, lock_type ltype, NEW_PE_INFO *pe_info) { if (!pe_info) return eval_lock_with(player, thing, ltype, NULL); else { PE_REGS *pe_regs; int result; pe_regs = pe_regs_localize(pe_info, PE_REGS_ISOLATE, "eval_lock_clear"); /* Run the lock */ result = eval_lock_with(player, thing, ltype, pe_info); /* Restore q-regs */ pe_regs_restore(pe_info, pe_regs); pe_regs_free(pe_regs); return result; } }
/** The say command. * \param player the enactor. * \param message the message to say. * \param pe_info pe_info to eval speechmod with */ void do_say(dbref player, const char *message, NEW_PE_INFO *pe_info) { dbref loc; PE_REGS *pe_regs; char modmsg[BUFFER_LEN]; char says[BUFFER_LEN]; char *sp; int mod = 0; loc = speech_loc(player); if (!GoodObject(loc)) return; if (!Loud(player) && !eval_lock_with(player, loc, Speech_Lock, pe_info)) { fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING); return; } if (*message == SAY_TOKEN && CHAT_STRIP_QUOTE) message++; pe_regs = pe_regs_create(PE_REGS_ARG, "do_say"); pe_regs_setenv_nocopy(pe_regs, 0, message); pe_regs_setenv_nocopy(pe_regs, 1, "\""); modmsg[0] = '\0'; if (call_attrib(player, "SPEECHMOD", modmsg, player, pe_info, pe_regs) && *modmsg != '\0') mod = 1; pe_regs_free(pe_regs); /* notify everybody */ notify_format(player, T("You say, \"%s\""), (mod ? modmsg : message)); sp = says; safe_format(says, &sp, T("%s says, \"%s\""), spname(player), (mod ? modmsg : message)); *sp = '\0'; notify_except(player, loc, player, says, NA_INTER_HEAR); }
/** Change an object's zone. * \verbatim * This implements @chzone. * \endverbatim * \param player the enactor. * \param name name of the object to change zone of. * \param newobj name of new ZMO. * \param noisy if 1, notify player about success and failure. * \param preserve was the /preserve switch given? * \param pe_info the pe_info for lock and permission checks * \retval 0 failed to change zone. * \retval 1 successfully changed zone. */ int do_chzone(dbref player, char const *name, char const *newobj, bool noisy, bool preserve, NEW_PE_INFO *pe_info) { dbref thing; dbref zone; int has_lock; if ((thing = noisy_match_result(player, name, NOTYPE, MAT_NEARBY)) == NOTHING) return 0; if (!newobj || !*newobj || !strcasecmp(newobj, "none")) zone = NOTHING; else { if ((zone = noisy_match_result(player, newobj, NOTYPE, MAT_EVERYTHING)) == NOTHING) return 0; } if (Zone(thing) == zone) { if (noisy) notify(player, T("That object is already in that zone.")); return 0; } if (!controls(player, thing)) { if (noisy) notify(player, T("You don't have the power to shift reality.")); return 0; } /* a player may change an object's zone to: * 1. NOTHING * 2. an object he controls * 3. an object with a chzone-lock that the player passes. * Note that an object with no chzone-lock isn't valid */ has_lock = (getlock(zone, Chzone_Lock) != TRUE_BOOLEXP); if (!(Wizard(player) || (zone == NOTHING) || controls(player, zone) || (has_lock && eval_lock_with(player, zone, Chzone_Lock, pe_info)))) { if (noisy) { if (has_lock) { fail_lock(player, zone, Chzone_Lock, T("You cannot move that object to that zone."), NOTHING); } else { notify(player, T("You cannot move that object to that zone.")); } } return 0; } /* Don't chzone object to itself for mortals! */ if ((zone == thing) && !Hasprivs(player)) { if (noisy) notify(player, T("You shouldn't zone objects to themselves!")); return 0; } /* Don't allow circular zones */ if (GoodObject(zone)) { dbref tmp; int zone_depth = MAX_ZONES; for (tmp = Zone(zone); GoodObject(tmp); tmp = Zone(tmp)) { if (tmp == thing) { notify(player, T("You can't make circular zones!")); return 0; } if (tmp == Zone(tmp)) /* Ran into an object zoned to itself */ break; zone_depth--; if (!zone_depth) { notify(player, T("Overly deep zone chain.")); return 0; } } } /* Don't allow chzone to objects without elocks! * If no lock is set, set a default lock (warn if zmo are used for control) * This checks for many trivial elocks (canuse/1, where &canuse=1) */ if (zone != NOTHING) check_zone_lock(player, zone, noisy); /* Warn Wiz/Royals when they zone their stuff */ if ((zone != NOTHING) && Hasprivs(Owner(thing))) { if (noisy) notify(player, T("Warning: @chzoning admin-owned object!")); } /* everything is okay, do the change */ Zone(thing) = zone; /* If we're not unzoning, and we're working with a non-player object, * we'll remove wizard, royalty, inherit, and powers, for security, unless * a wizard is changing the zone and explicitly says not to. */ if (!Wizard(player)) preserve = 0; if (!preserve && ((zone != NOTHING) && !IsPlayer(thing))) { /* if the object is a player, resetting these flags is rather * inconvenient -- although this may pose a bit of a security * risk. Be careful when @chzone'ing wizard or royal players. */ clear_flag_internal(thing, "WIZARD"); clear_flag_internal(thing, "ROYALTY"); clear_flag_internal(thing, "TRUST"); destroy_flag_bitmask("POWER", Powers(thing)); Powers(thing) = new_flag_bitmask("POWER"); } else { if (noisy && (zone != NOTHING)) { if (Hasprivs(thing)) notify(player, T("Warning: @chzoning a privileged player.")); if (Inherit(thing)) notify(player, T("Warning: @chzoning a TRUST player.")); } } if (noisy) notify(player, T("Zone changed.")); return 1; }
/** Link an exit, room, player, or thing. * \verbatim * This is the top-level function for @link, which is used to link an * exit to a destination, set a player or thing's home, or set a * drop-to on a room. * * Linking an exit usually seizes ownership of the exit and costs 1 penny. * 1 penny is also transferred to the former owner. * \endverbatim * \param player the enactor. * \param name the name of the object to link. * \param room_name the name of the link destination. * \param preserve if 1, preserve ownership and zone data on exit relink. * \param pe_info the pe_info to use for any lock checks */ void do_link(dbref player, const char *name, const char *room_name, int preserve, NEW_PE_INFO *pe_info) { /* Use this to link to a room that you own. * It usually seizes ownership of the exit and costs 1 penny, * plus a penny transferred to the exit owner if they aren't you. * You must own the linked-to room AND specify it by room number. */ dbref thing; dbref room; if (!room_name || !*room_name) { do_unlink(player, name); return; } if (!IsRoom(player) && GoodObject(Location(player)) && IsExit(Location(player))) { notify(player, T("You somehow wound up in a exit. No biscuit.")); return; } if ((thing = noisy_match_result(player, name, TYPE_EXIT, MAT_EVERYTHING)) != NOTHING) { switch (Typeof(thing)) { case TYPE_EXIT: if ((room = check_var_link(room_name)) == NOTHING) room = parse_linkable_room(player, room_name, pe_info); if (room == NOTHING) return; if (GoodObject(room) && !can_link_to(player, room, pe_info)) { notify(player, T("Permission denied.")); break; } /* We may link an exit if it's unlinked and we pass the link-lock * or if we control it. */ if (!(controls(player, thing) || ((Location(thing) == NOTHING) && eval_lock_with(player, thing, Link_Lock, pe_info)))) { notify(player, T("Permission denied.")); return; } if (preserve && !Wizard(player)) { notify(player, T("Permission denied.")); return; } /* handle costs */ if (Owner(thing) == Owner(player)) { if (!payfor(player, LINK_COST)) { notify_format(player, T("It costs %d %s to link this exit."), LINK_COST, ((LINK_COST == 1) ? MONEY : MONIES)); return; } } else { if (!payfor(player, LINK_COST + EXIT_COST)) { int a = LINK_COST + EXIT_COST; notify_format(player, T("It costs %d %s to link this exit."), a, ((a == 1) ? MONEY : MONIES)); return; } else if (!preserve) { /* pay the owner for his loss */ giveto(Owner(thing), EXIT_COST); chown_object(player, thing, player, 0); } } /* link has been validated and paid for; do it */ if (!preserve) { Owner(thing) = Owner(player); Zone(thing) = Zone(player); } Location(thing) = room; /* notify the player */ notify_format(player, T("Linked exit #%d to %s"), thing, unparse_object(player, room, AN_UNPARSE)); break; case TYPE_PLAYER: case TYPE_THING: if ((room = noisy_match_result(player, room_name, NOTYPE, MAT_EVERYTHING)) == NOTHING) { notify(player, T("No match.")); return; } if (IsExit(room)) { notify(player, T("That is an exit.")); return; } if (thing == room) { notify(player, T("You may not link something to itself.")); return; } /* abode */ if (!controls(player, room) && !Abode(room)) { notify(player, T("Permission denied.")); break; } if (!controls(player, thing)) { notify(player, T("Permission denied.")); } else if (room == HOME) { notify(player, T("Can't set home to home.")); } else { /* do the link */ Home(thing) = room; /* home */ if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player))) notify(player, T("Home set.")); } break; case TYPE_ROOM: if ((room = parse_linkable_room(player, room_name, pe_info)) == NOTHING) return; if ((room != HOME) && (!IsRoom(room))) { notify(player, T("That is not a room!")); return; } if (!controls(player, thing)) { notify(player, T("Permission denied.")); } else { /* do the link, in location */ Location(thing) = room; /* dropto */ notify(player, T("Dropto set.")); } break; default: notify(player, T("Internal error: weird object type.")); do_log(LT_ERR, NOTHING, NOTHING, "Weird object! Type of #%d is %d", thing, Typeof(thing)); break; } } }
/** The oemit(/list) command. * \verbatim * This implements @oemit and @oemit/list. * \endverbatim * \param executor The object \@oemit'ing * \param speaker The object making the sound (executor, unless /spoof'ing) * \param list the list of dbrefs to oemit from the emit. * \param message the message to emit. * \param flags PEMIT_* flags. * \param format a format_msg structure to pass to notify_anything() from \@message * \param pe_info the pe_info to use for evaluating speech locks */ void do_oemit_list(dbref executor, dbref speaker, char *list, const char *message, int flags, struct format_msg *format, NEW_PE_INFO *pe_info) { char *temp, *p; const char *s; dbref who; dbref room; int matched = 0; dbref pass[11]; dbref locs[10]; int i, oneloc = 0; int na_flags = NA_INTER_HEAR | NA_PROPAGATE; /* If no message, further processing is pointless. * If no list, they should have used @remit. */ if (!message || !*message || !list || !*list) return; if (flags & PEMIT_SPOOF) na_flags |= NA_SPOOF; for (i = 0; i < 11; i++) pass[i] = NOTHING; /* Find out what room to do this in. If they supplied a db# before * the '/', then oemit to anyone in the room who's not on list. * Otherwise, oemit to every location which has at least one of the * people in the list. This is intended for actions which involve * players who are in different rooms, e.g.: * * X (in #0) fires an arrow at Y (in #2). * * X sees: You fire an arrow at Y. (pemit to X) * Y sees: X fires an arrow at you! (pemit to Y) * #0 sees: X fires an arrow at Y. (oemit/list to X Y) * #2 sees: X fires an arrow at Y. (from the same oemit) */ /* Find out what room to do this in. They should have supplied a db# * before the '/'. */ if ((temp = strchr(list, '/'))) { *temp++ = '\0'; room = noisy_match_result(executor, list, NOTYPE, MAT_EVERYTHING); if (!GoodObject(room)) { notify(executor, T("I can't find that room.")); return; } if (!Loud(speaker) && !eval_lock_with(speaker, room, Speech_Lock, pe_info)) { fail_lock(executor, room, Speech_Lock, T("You may not speak there!"), NOTHING); return; } oneloc = 1; /* we are only oemitting to one location */ } else { temp = list; } s = temp; while (s && *s) { p = next_in_list(&s); /* If a room was given, we match relative to the room */ if (oneloc) who = match_result_relative(executor, room, p, NOTYPE, MAT_OBJ_CONTENTS); else who = noisy_match_result(executor, p, NOTYPE, MAT_OBJECTS); /* matched tracks the number of valid players we've found. * room is the given room (possibly nothing right now) * pass[0..10] are dbrefs of players * locs[0..10] are corresponding dbrefs of locations * pass[11] is always NOTHING */ if (GoodObject(who) && GoodObject(Location(who)) && (Loud(speaker) || (oneloc && Location(who) == room) || eval_lock_with(speaker, Location(who), Speech_Lock, pe_info)) ) { if (matched < 10) { locs[matched] = Location(who); pass[matched] = who; matched++; } else { notify(executor, T("Too many people to oemit to.")); break; } } } if (!matched) { if (oneloc) { /* A specific location was given, but there were no matching objects to * omit, so just remit */ notify_anything(executor, speaker, na_loc, &room, NULL, na_flags, message, NULL, room, format); } else { notify(executor, T("No matching objects.")); } return; } /* Sort the list of rooms to oemit to so we don't oemit to the same * room twice */ qsort((void *) locs, matched, sizeof(locs[0]), dbref_comp); for (i = 0; i < matched; i++) { if (i != 0 && locs[i] == locs[i - 1]) continue; notify_anything(executor, speaker, na_loc, &locs[i], pass, na_flags, message, NULL, locs[i], format); } }